使用 DI 注入一些参数,并手动分配一些参数

Posted

技术标签:

【中文标题】使用 DI 注入一些参数,并手动分配一些参数【英文标题】:Having some parameters Injected with DI and some assigned manually 【发布时间】:2021-11-28 11:15:03 【问题描述】:

在 .NET Core 3.1 控制台应用程序中,我想要一个在构造函数中注入一些参数但我可以手动分配的类。例如类似的东西,但注入了 IConfiguration:

static void Main() 
    var myObj1 = new MyClass(1);
    var myObj2 = new MyClass(2);


public class MyClass 
    public MyClass(IConfiguraiton config, int myVal)
    

     

我用 Ninject 试过这个:

static void Main()

    kernel = new StandardKernel();
    kernel.Load(Assembly.GetExecutingAssembly());
    kernel.Get<MyClass>();


public class MyClass

    public MyClass(IConfiguraiton config)
    

    


public class Bindings : NinjectModule

    public override void Load()
    
       var configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.json").Build();

       Bind<IConfiguration>().ToMethod(ctx => SetupConfiguration()).InSingletonScope();
       Bind<MyClass>().ToSelf().InTransientScope();
    

我设法进行了简单的依赖注入,但没有成功使用参数进行注入。

我读过很多人建议最好将参数传递给类的方法而不是构造函数,但是在我的情况下,这不是一个选择,另外我是一名软件工程专业的学生,​​并且希望了解如何执行此操作,因为它在某些情况下可能很有用。

【问题讨论】:

如果你想自己创建依赖注入有什么意义?将这些值封装到注入到类中的另一个配置提供程序等中。 是的,这个有点不适合。你可以做些什么来解决它是使用属性注入(NInject 支持它),然后像var my = new MyClass myProp = 1 ; 那样在初始值中提供你的其他属性。然而,属性注入通常是不受欢迎的,因为开发人员不清楚哪些东西被注入了,哪些东西没有被注入,并且在测试代码时很容易错过一些 另外 - however in my situation this isn't an option in addition - 为什么不呢? 注入值的作用是什么?这个值是恒定的,在应用程序运行时不改变,并且在启动时可用,或者更确切地说是一个可能在每个请求上改变的运行时值。或者可能介于两者之间。请描述其功能和起源;正确的解决方案在很大程度上取决于您给出的答案。 好的,很抱歉关于上下文的信息太少了。所以 MyClass 将是一个负责下载网站文件的类。因此,我想要传递的值将是指向该网站的链接。我不想将值传递给方法,因为在这个类中会有很多方法使用链接,所以使用构造函数会更有意义吗?我不能在配置中拥有该值,因为我将创建该类的多个实例来从不同的网站下载文件。 【参考方案1】:

这是Ninject.Extensions.Factory 很有用的情况,因为它正是为这种情况而设计的。除了 Castle.Core, as it uses DynamicProxy 之外,它还引入了 Factory 依赖项(作为 SE 学生,使用此库是使用 interceptor pattern 的好主意)。

要使用它,您可以像这样定义一个工厂接口:

public interface IMyClassFactory

    MyClass Create(int myVal);

注意Create 方法返回MyClassCreate 方法的参数在类型和名称上与您希望提供的参数完全匹配。您要注入的参数类型必须在内核中注册。不幸的是,这里很容易出错——如果你指定了一个在工厂接口中不存在的参数,它会被忽略,但如果你忘记了一个参数,它会在调用时抛出异常。

接下来,像这样注册IMyClassFactoryBind&lt;IMyClassFactory&gt;().ToFactory(); 并删除您对MyClass 的绑定。然后在需要创建实例的任何地方注入IMyClassFactory 并调用Create:kernel.Get&lt;IMyClassFactory&gt;().Create(2)

您可以在不使用Ninject.Extensions.Factory 的情况下通过编写和注册您自己的IMyClassFactory 实现来获得相同的结果,基本上与Factory 扩展最终发出的代码相同。下面是一个完整的示例,使用基于注释输入/输出注册的两种方法(如果将 .InSingletonScope() 添加到 IConfiguraiton 的注册中,请注意输出 - 两种方法都尊重 Ninject 的绑定范围)。

internal class Program

    static void Main(string[] args)
    
        var kernel = new StandardKernel();
        kernel.Bind<IConfiguraiton>().To<Configuraiton>();
        kernel.Bind<IMyClassFactory>().ToFactory();
        //kernel.Bind<IMyClassFactory>().To<NinjectMyClassFactory>().InSingletonScope();
        var factory = kernel.Get<IMyClassFactory>();
        var one = factory.Create(1);
        var two = factory.Create(2);
    

public interface IMyClassFactory

    MyClass Create(int myVal);

public class NinjectMyClassFactory : IMyClassFactory

    public NinjectMyClassFactory(IResolutionRoot resolutionRoot)
    
        ResolutionRoot = resolutionRoot;
    
    private IResolutionRoot ResolutionRoot  get; 
    public MyClass Create(int myVal)
    
        return ResolutionRoot.Get<MyClass>(new ConstructorArgument("myVal", myVal));
    

public class MyClass

    public MyClass(IConfiguraiton config, int myVal)
    
        Console.Out.WriteLine("Created MyClass(0,1)", config.MyNum, myVal);
    

public interface IConfiguraiton  int MyNum  get;  
public class Configuraiton : IConfiguraiton

    static int CreateCount;
    public Configuraiton()
    
        MyNum = Interlocked.Increment(ref CreateCount);
    
    public int MyNum  get; 

【讨论】:

以上是关于使用 DI 注入一些参数,并手动分配一些参数的主要内容,如果未能解决你的问题,请参考以下文章

企业检测SQL注入的一些方式探讨

Symfony依赖注入将类型的所有类作为参数注入

Spring重温--Spring依赖注入(DI)

大话设计模式--DI(依赖注入)

依赖注入,带参数注入

iOS控制反转(IoC)与依赖注入(DI)的实现