Autofac - 预防"手册"实例化一个类

时间:2017-02-07 00:22:11

标签: c# dependency-injection autofac

我有一个类,我希望Autofac成为唯一一个可以解析实例的人。

我想这样做new()在普通的C#代码中使用(无论是在编译时还是在运行时)。

该类不接受任何依赖。

我该如何实现?我尝试使用private构造函数。它有助于在编译时阻止new(),但Autofac无法使用反射来实例化它并失败。

一个尴尬的选择可能是通过类构造函数的参数人为地添加一个依赖项,这将隐式告诉开发人员new()将会很难并且可能不会意外地做到这一点,但它& #39;远非理想,因为:

  1. 他们仍然可以在技术上实例化(我管理员说这个参数对所有其他普通的DI启用类也有效...)
  2. 因为这个而闻起来添加一个参数。

4 个答案:

答案 0 :(得分:2)

在类的构造函数中,您可以遍历调用堆栈,直到找到调用AutoFac的Resolve方法。如果找不到,那就错了。

public class MyClass
{
    private void AssertAutoFac()
    {
        var trace = new StackTrace();
        for (int i=2; i<trace.FrameCount; i++)  //Start with i=2, skipping last two stack entries, which will be internal to this class anyway
        { 
            var method = trace.GetFrame(i).GetMethod();
            if (method.Name == "Resolve") 
            {
                var dll = method.DeclaringType.Assembly.FullName;
                if (dll == "Autofac, Version=1.4.4.561, Culture=neutral, PublicKeyToken=17863af14b0044da") return;  
            }
        }
        throw new InvalidOperationException("You must instantiate using AutoFac!");
    }

    public MyClass()
    {
        AssertAutoFac();
    }
}

=== OP编辑===

以上作品很棒。它的较短版本可能是:

var autofacInstantiated = trace.GetFrames().Skip(2).Any(t=> t.GetMethod().DeclaringType != null && t.GetMethod().DeclaringType.Namespace.StartsWith("Autofac"));
    if (!autofacInstantiated)
        throw new InvalidOperationException("You must instantiate using AutoFac!");

需要注意的是,它不会检查非常特定的Autofac程序集,因此,理论上,人们可以在(假)Autofac.*命名空间内伪造一个方法并欺骗这个机制。但是,如果您想要的是对未来开发人员进行意外手动实例化的完整性保护,那就可以解决这个问题。

此外,这个版本可以容忍Autofac版本更新(不过第一个代码片段也可以相应修改)。

答案 1 :(得分:2)

你无法阻止某人在运行时调用构造函数,因为反射允许这样做。

我真的不确定你为什么要把构造函数保密。在我看来,它概述了其他一些问题,但回答你的问题,这就是你如何做到的。给出以下类定义:

public class MyClass
{
    private MyClass()
    {
    }
}

您可以在Autofac中以这种方式注册:

var privateMyClassConstructor = typeof(MyClass).GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance,
    null,
    Type.EmptyTypes,
    null);

var builder = new ContainerBuilder();

builder
    .Register(x => (MyClass)privateMyClassConstructor.Invoke(null))
    .AsSelf();

var container = builder.Build();

var firstMyClassInstance = container.Resolve<MyClass>();
var secondMyClassInstance = container.Resolve<MyClass>();

我建议通过注册之外的反射保留构造函数的发现,因为它应该很慢并且你只想这样做一次。

答案 2 :(得分:2)

我认为这是最简单的方法:

[Obsolete("This constructor is for Autofac only")]
public MyConstructor()
{
}

如果有人明确地调用它会产生警告,但Autofac仍将使用构造函数。希望警告与项目中的错误一样无法容忍。

答案 3 :(得分:1)

假设感兴趣的类在库中而不在主程序中,您可以使用属性来控制范围。

  1. 保持课程public
  2. 制作所有构造函数internal
  3. 为了让AutoFac能够调用内部构造函数,请使用InternalsVisibleToAttribute启用AutoFac来访问它们。

    //Expose all internal members to AutoFac
    [assembly: InternalsVisibleTo("Autofac")]
    public class MyClass
    {
        internal MyClass()
        {
        }
    }