Autofac可选/惰性依赖项 [英] Autofac optional/lazy dependencies

查看:134
本文介绍了Autofac可选/惰性依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

If I put Lazy in constructor of my object, and X is not registered in container I got dependency resolution exception.

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

Why am I getting this exception? I dislike it, because I cannot choose component at runtime. Example usecase:

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管理.

To do so I need to register both components A an B. My program fails even if B is never used. I would like to have B be optional, and still managed by autofac.

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

This is even bigger issue if I have list of components, and want only one to be used. For example:

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

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

    }

}

我不再因为没有注册某些东西而获得例外,但是仍然构建了所有东西.而且使用起来也很尴尬.

I am no longer getting exception is something is not registered, however still everything is constructed. Also it would be awkward to use.

推荐答案

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

If you add a reference to a Lazy<T> component, Autofac has to know (basically) how to create the internal function that will run should you want to resolve it, even if you don't resolve it.

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

Basically, it needs to be able to create this in memory:

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

Autofac要求您注册所有要解析的内容.它不允许您即时注册事物-它必须是明确的.因此,您尝试执行的操作将无法正常工作(如您所见).

Autofac requires all of the things you want to resolve to be registered. It doesn't let you register things on the fly - it must be explicit. So the thing you're trying to do won't work (as you saw).

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

Instead, use a single interface and two different implementations of that interface. Change the registration based on your configuration value.

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

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

Then in your controller, inject the interface rather than the concrete class.

public MyController(Lazy<IService> service)

您还可以执行其他选择,例如将元数据用于组件或关键服务.例如,您可以根据自己的配置添加一些元数据,然后使用该元数据进行解析.

There are also other options you could do, like use metadata for components or keyed services. For example, you could add some metadata based on your configuration and resolve using that.

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

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

In the controller, you'd get a dictionary of them:

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

这是一个非常简短的示例,但是文档显示了更多.

That's a very short example, but the docs show a lot more.

无论如何,界面实际上将是您成功的关键.如果您仅使用具体的类,则无论是否使用它们都必须进行注册.

In any case, the interface is really going to be your key to success. If you're just using concrete classes, they'll have to be registered whether you use them or not.

这篇关于Autofac可选/惰性依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆