.net 如何在类库中使用依赖注入?

6bc51xsx  于 2023-03-04  发布在  .NET
关注(0)|答案(2)|浏览(219)

我已经在一个.NET Framework 4.8类库中安装了Microsoft依赖注入。我还将接口A注册到类A。问题是我的类B在构造器中接受接口A的对象,当我试图创建该类时,它仍然需要它。那么,我如何让DI系统在一个没有真实的入口点的类库中提供这个对象呢?

public Class B
{
   B(){InterfaceA}
}

public Class A : InterfaceA
{}

public MainClass
{
    public void DoStuff()
    {
       DependencyContainer.Register();
       var myB = new B();
    }
}

public DependencyContainer
{
     public void Register()
     {
         (new ServiceCollection()).AddTransient<InterfaceA, A>();
     }
}

问候

goqiplq2

goqiplq21#

DI意味着任何依赖项都将从外部注入。类本身甚至不知道依赖项注入的使用。在您的情况下,应该重写类以接受依赖项,而不是创建依赖项:

public Class B
{
   public A MyA {get;}

   public B(InterfaceA a)
   {
      MyA=a;
   }
}

public Class A : InterfaceA
{}

public MainClass
{
    public B MyB {get;}
    public MainClass(B b)
    {
        MyB=b;
    }

    public void DoStuff()
    {
       MyB......;
    }
}

类对DI一无所知。当请求MainClass示例时,例如services.GetRequiredService<MyClass>(),DI容器将创建所有必要的示例并将它们传递给类。
所有.NET核心库的共同模式是提供扩展方法,这些方法可用于在主应用程序的Startup类或ConfigureServices委托中注册或配置它们的类。要做到这一点,只需要Microsoft.Extensions.DependencyInjection.Abstractions包中的IServicesCollection接口。无需添加完整的DI包:

public static class MyLibServiceExtensions
{
    public static IServiceCollection AddMyClasses(this IServiceCollection services)
    {
        services.AddTransient<ClassB>()
                .AddTransient<InterfaceA,ClassA>()
                .AddTransient<MainClass>();
        return services;
    }
}

现在可以使用此命令注册类,例如在控制台应用程序中:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddMyClasses()
                        .AddHostedService<Worker>();
            });
}

或者Web应用程序的Startup.ConfigureServices方法:

public class Startup
{
    public Startup(IWebHostEnvironment env)
    {
        HostingEnvironment = env;
    }

    public IWebHostEnvironment HostingEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        ...

        services.AddMyClasses();
    }
}

类现在将被注入到DI容器生成的任何类中,例如"Controller:

public class MyController:ControllerBase
{
    public MyController(MyDbContext dbContext,MainClass main)
    {
        _db=dbContext;
        _main=main;
    }
}
8dtrkrch

8dtrkrch2#

好了,依赖注入并不是魔术,你的Register方法构建了一个服务集合,然后丢弃了它,你需要从容器中解析你的组件(我不是直接说,但是容器必须参与进来)。
您应该使用BuildServiceProvider方法从它构建一个IServiceProvider

var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<InterfaceA, A>();
serviceCollection.AddTransient<B>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var b = serviceProvider.GetRequiredService<B>(); // constructs B and injects A

也许应该在Main方法中执行此操作,然后将B(或一个Func来创建B)注入MainClass

static void Main(string[] args)
{
    var serviceCollection = new ServiceCollection();
    serviceCollection.AddTransient<MainClass>();
    serviceCollection.AddTransient<InterfaceA, A>();
    serviceCollection.AddTransient<B>();
    serviceCollection.AddTransient<Func<B>>(provider => () => provider.GetRequiredService<B>()); // factory method
    var serviceProvider = serviceCollection.BuildServiceProvider();

    // ask the container for MainClass and then call DoStuff
    var mainClass = serviceProvider.GetRequiredService<MainClass>();
    mainClass.DoStuff();
}

然后你可以像这样重写MainClass:

public class MainClass
{
    private readonly B _bInstance;
    private readonly Func<B> _bFactory = new Func<B>();
    
    // if you want to create instances of B as and when, I'd suggest using a factory
    // otherwise you can pass in a single instance
    // I've done both here as an example
    public MainClass(Func<B> bFactory, B bInstance)
    {
        _bInstance = bInstance;
        _bFactory = bFactory;
    }

    public void DoStuff()
    {
        // create two instances of B
        var b1 = _bFactory();
        var b2 = _bFactory();
    }
}

虽然我提倡在需要动态构建示例时使用工厂,但也可以将IServiceProvider本身注入到类中,然后调用它的GetRequiredServiceGetService方法,我不这样做的原因是因为它是considered to be an anti-pattern,使用工厂允许您仍然在组合根上进行更改(即在Main中),而无需编辑创建B示例的所有位置。

相关问题