Autofac可选/惰性依赖

时间:2017-01-25 16:21:00

标签: c# autofac lazy-initialization

如果我把Lazy放在我的对象的构造函数中,并且X没有在容器中注册,我得到了依赖项解析异常。

为什么我会收到此异常?我不喜欢它,因为我无法在运行时选择组件。用例示例:

class Controller
{
   public Controller(Lazy<A> a, Lazy<B> b) { /* (...) */ }

 Lazy<A> a;
 Lazy<B> b;

 public IActionResult Get(){
  if(someConfig)
    return Json(a.Value.Execute());
  else
    return Json(b.Value.Execute());
 }
}

为此,我需要注册组件A和B.即使从未使用过B,我的程序也会失败。我希望B是可选的,仍然由autofac管理。

如果我有组件列表,并且只想使用一个组件,这就是更大的问题。例如:

class Controller
{
    Controller(IEnumerable<Component> components) { /* (...) */ }

    IActionResult Get()
    {
        return components.First(n => n.Name == configuredComponent).Execute();

    }

}

我不再得到异常是没有注册的东西,但是仍然构建了一切。使用起来也很尴尬。

1 个答案:

答案 0 :(得分:8)

如果您添加对Lazy<T>组件的引用,Autofac必须(基本上)知道如何创建将运行的内部函数,如果您想解决它,即使您不要解决它。

基本上,它需要能够在内存中创建它:

var lazy = new Lazy<T>(() => scope.Resolve<T>());

Autofac需要注册您要解决的所有内容。它不会让你即时注册 - 它必须是明确的。因此,您尝试做的事情不会起作用(如您所见)。

相反,使用单个接口和该接口的两个不同实现。根据您的配置值更改注册。

var builder = new ContainerBuilder();
if(someConfig)
{
  builder.RegisterType<A>().As<IService>();
}
else
{
  builder.RegisterType<B>().As<IService>();
}

然后在你的控制器中,注入接口而不是具体的类。

public MyController(Lazy<IService> service)

您还可以执行其他选项,例如对组件使用metadatakeyed services。例如,您可以根据您的配置添加一些元数据并使用它进行解析。

builder.RegisterType<A>()
       .As<IService>()
       .WithMetadata("Name", "a");
builder.RegisterType<B>()
       .As<IService>()
       .WithMetadata("Name", "b");

在控制器中,您可以获得它们的字典:

public MyController(IEnumerable<Meta<IService>> services)
{
  var service = services.First(s => s.Metadata["Name"].Equals(someConfig);
}

这是一个非常简短的例子,但the docs show a lot more

无论如何,界面确实是你成功的关键。如果你只是使用具体的课程,无论你是否使用它们,都必须注册。