使用Autofac自动将构造函数参数分配给成员变量?

时间:2018-03-27 15:07:06

标签: dependency-injection constructor autofac

使用Autofac时,我发现自己写了很多这样的样板代码:

public class MyClass {
    [...]

    public MyClass(IFoo foo, IBar bar, IBaz baz) {
        _foo = foo;
        _bar = bar;
        _baz = baz;
    }
}

是否有一种方法可以根据名称约定(自动映射程序)使用Autofac自动将构造函数注入的依赖项分配给它们的等效成员变量?或者也许您可以使用属性告诉Autofac它应该注入哪些属性,例如:

[DependencyInjected]
private IFoo _foo;

[DependencyInjected]
private IBar _bar;

[DependencyInjected]
private IBaz _baz;

1 个答案:

答案 0 :(得分:2)

  

是否有一种方法可以根据名称约定(自动映射程序)使用Autofac自动将构造函数注入的依赖项分配给它们的等效成员变量?

不,除非你自己建造它。 Autofac仅与标准OOP实践兼容,不包括使用Reflection来填充私有成员变量。

implementing the Dependency Injection pattern in C#有3种不同的方式可以注入依赖项时:

  1. 构造函数注入
  2. Property Injection(也称为Setter注入)
  3. 方法注入
  4. 没有接受模式用于注入private成员变量。实际上,这不能用标准的OO原则来完成,在.NET中它只能使用Reflection来完成。

    此外,在使用依赖注入模式时,没有规则说必须使用DI容器,例如Autofac。实际上,在应用DI图案时,DI容器的使用完全是可选。例如,在进行单元测试时,很容易使用没有Autofac的模式 - 这是一个使用NUnit和Moq的例子。

    [Test]
    public void TestDoSomething()
    {
        // Arrange
        var foo = new Mock<IFoo>();
        var bar = new Mock<IBar>();
        var baz = new Mock<IBaz>();
        var target = new MyClass(foo.Object, bar.Object, baz.Object);
    
        // Act
        var result = target.DoSomething();
    
        // Assert
        Assert.IsNotNull(result);
        // Test other conditions
    }
    

    另一方面,如果你添加这一额外的反射,你需要访问它才能填充你的MyClass依赖项。

    [Test]
    public void TestDoSomething()
    {
        // Arrange
        var foo = new Mock<IFoo>();
        var bar = new Mock<IBar>();
        var baz = new Mock<IBaz>();
        var target = new MyClass();
    
        // Use Reflection to insert the dependencies into the object
        InjectDependencies(target, foo.Object, bar.Object, baz.Object);
    
        // Act
        var result = target.DoSomething();
    
        // Assert
        Assert.IsNotNull(result);
        // Test other conditions
    }
    

    这里的一个重要问题是,如果您完全删除InjectDependencies(target, foo, bar, baz);行,代码将编译正常,并且您可能会在运行时的某个位置最终得到NullReferenceException类。 instance constructor的目的是:

      

    在使用新表达式创建类的对象时创建和初始化任何实例成员变量。

    这可以保证正确构造对象及其所有依赖项。具有构造函数注入的DI模式的典型示例:

    public class MyClass 
    {
        private readonly IFoo _foo;
        private readonly IBar _bar;
        private readonly IBaz _baz;
    
        public MyClass(IFoo foo, IBar bar, IBaz baz) {
            _foo = foo ?? throw new ArgumentNullException(nameof(foo));
            _bar = bar ?? throw new ArgumentNullException(nameof(bar));
            _baz = baz ?? throw new ArgumentNullException(nameof(baz));
        }
    }
    

    上面的示例使用构造函数中的 readonly关键字保护条款(您的示例中缺少这些)保证 MyClass的实例无法创建,除非提供了所有依赖项。换句话说,任何成员变量都有{0}的概率为0 {0},因此您不必担心通过向其余成员添加null检查来增加代码的复杂性。班上的。构造函数之外的任何代码都有0%的可能更改任何依赖项,这可能会导致应用程序难以找到稳定性问题。

    最重要的是,如果您想在整个班级中构建自己的Autofac扩展并编写null检查,那么可以使用Reflection以这种方式填充您的类,但我不会不推荐它,因为你正在使用纯OOP实现松散耦合并将其转换为一个紧密耦合的Reflection代码,所有类(以及使用它们的任何人)都将依赖它根据。

    您也正在消除使用C#的便捷功能的可能性,这些功能可以保证实例始终具有所有依赖关系,无论使用它的上下文(DI容器或没有DI容器)并且它们不能是在运行时无意中替换或设置为null

    解决方法

    如果您对“样板代码”的主要关注是您必须自己键入构造函数代码,那么以下是一些自动执行该部分的Visual Studio扩展:

    enter image description here

    不幸的是,他们似乎都没有将guard子句添加到构造函数中。