Autofac-解析运行时参数,而不必传递容器

问题描述 投票:25回答:3

我有一个更简单的“ ServiceHelper”类,该类在构造函数中带有两个参数:

public ServiceHelper(ILogger<ServiceHelper> log, string serviceName)

((Autofac提供的NLog的ILogger通用包装很好,并且serviceName是Windows服务的名称,用于控制我需要在运行时提供的控件。)

我无法解决如何在运行时如何使用Autofac传递不同的服务名称来创建此类的新实例的问题。这样的事情当然是行不通的,因为我需要在运行时指定不同的服务名称:

builder.RegisterType<ServiceHelper>().As<IServiceHelper>().WithParameter(new NamedParameter("serviceName", null)).InstancePerDependency();

根据我所读的内容,将容器传递过来并手动手动调用Resolve(AutoFac警告的Service Locator“反模式”)是个坏习惯,是吗?如果我做到了,那么我可以做到

container.Resolve<ServiceHelper>(new NamedParameter("serviceName", "some service name"));

但是要想走得更远,我还不确定如何使Autofac将该容器注入类,它只需要注册自己像这样精确?然后让我的类在其构造函数中需要IContainer吗? (这是在使用构造函数注入的C#服务中)

builder.RegisterType<Container>().As<IContainer>().InstancePerDependency();

我也阅读了有关委托工厂的信息,但这似乎并没有使容器必须四处传递。

我的大多数使用ServiceHelper的类,实际上只需要1或2个ServiceHelper来提供特定的服务名称,因此,它不像我要用意想不到的serviceName参数来成​​千上万,这只是让我的头有点受伤。

c# autofac
3个回答
24
投票
是的,将容器传递到任何地方都是一种反模式。

您可以通过使用这样的工厂来避免这种情况:

((注:此答案中的所有代码均未经测试,我正在使用不带Visual Studio的计算机上的文本编辑器中编写此代码)

public interface IServiceHelperFactory { IServiceHelper CreateServiceHelper(string serviceName); } public class ServiceHelperFactory : IServiceHelperFactory { private IContainer container; public ServiceHelperFactory(IContainer container) { this.container = container; } public IServiceHelper CreateServiceHelper(string serviceName) { return container.Resolve<ServiceHelper>(new NamedParameter("serviceName", serviceName)); } }
[启动时,您像其他所有操作一样在Autofac中注册ServiceHelperFactory

builder.RegisterType<ServiceHelperFactory>().As<IServiceHelperFactory>();

然后,当您在其他地方需要ServiceHelper时,可以通过构造函数注入来获取工厂:

public class SomeClass : ISomeClass { private IServiceHelperFactory factory; public SomeClass(IServiceHelperFactory factory) { this.factory = factory; } public void ThisMethodCreatesTheServiceHelper() { var helper = this.factory.CreateServiceHelper("some service name"); } }

通过使用Autofac通过构造函数注入创建工厂本身,您可以确保工厂了解容器,而不必自己传递容器。

我承认,乍看之下,此解决方案与直接将容器传递过来看起来没有什么不同。但优点是您的应用仍与容器分离-唯一已知容器(启动除外)的地方是工厂内部。


编辑:

好的,我忘了。如上所述,我是在没有Visual Studio的计算机上编写此代码的,因此无法测试示例代码。现在,我阅读了您的评论,我记得当我使用Autofac并尝试注册容器本身时,我遇到了类似的问题。

我的问题是我需要在构建器中注册容器。但是要获取容器实例进行注册,我需要调用builder.Build() ...这将创建容器,这意味着之后我无法在构建器中注册内容。我不记得收到的错误消息了,但我想您现在有同样的问题。

我发现的解决方案是创建第二个构建器,在此处注册容器,

然后使用第二个构建器更新一个也是唯一的容器

这是我的一个开源项目中的工作代码:

On startup, I register the container:

var builder = new ContainerBuilder(); // register stuff here var container = builder.Build(); // register the container var builder2 = new ContainerBuilder(); builder2.RegisterInstance<IContainer>(container); builder2.Update(container);

...然后用于by a WindowService to create new WPF windows

WindowService


7
投票
我沿用了上述方法,并且运行良好,但是由于IContainer中的“ Resolve <>”方法是扩展方法,因此我发现无法进行单元测试。在谈论所有关于不通过容器的讨论时,它也从未真正感到“正确”。

我回到了绘图板上,找到了使用Autofac代理工厂实例化对象的“正确”方法。public class WindowService : IWindowService { private readonly IContainer container; public WindowService(IContainer container) { this.container = container; } public T GetWindow<T>() where T : MetroWindow { return (T)this.container.Resolve<T>(); } }


3
投票
仅对根组合对象发生解析。调用resolve几乎与“新建”对象相同,这是一种气味测试。有时,分辨率是动态的,只能即时确定,但是

most依赖性是确定性的,可以预先注册。如何使用Autofac做到这一点是挑战。 @Christian Specht授予的答案是一个很好的答案,但它假定一切都在运行时确定。

要在设计时定义依赖关系链,请参见SO主题http://docs.autofac.org/en/latest/advanced/delegate-factories.html ...
© www.soinside.com 2019 - 2024. All rights reserved.