[英]Using Autofac, how can I resolve a collection of services and ignore ones that fail to resolve?
I'm trying to see if there's a way I can ask Autofac to resolve all implementations of a particular type ( IEnumerable<TService>
), returning all implementations that can be successfully resolved while silently ignoring those that fail . 我正在尝试查看是否有一种方法可以要求Autofac解析特定类型(
IEnumerable<TService>
)的所有实现,返回可以成功解析的所有实现, 同时静默忽略失败的实现 。
In one of my libraries I define an ICommunicationService
interface which I implement in various other places. 在我的一个库中,我定义了一个
ICommunicationService
接口,该接口在其他地方实现。 I've created a "test bed" ASP.NET Core web service that I can use to pick up all implementations of this interface among its dependencies and allow me to test them. 我已经创建了一个“测试平台” ASP.NET Core Web服务,可以用来在依赖关系中选择该接口的所有实现,并允许我对其进行测试。
When I try to access the selection page in the browser, the web service uses the following query to find all candidates: 当我尝试在浏览器中访问选择页面时,Web服务将使用以下查询来查找所有候选者:
var query = from service in container.Resolve<IEnumerable<ICommunicationService>>()
let serviceType = service.GetType()
let serviceAssembly = serviceType.GetTypeInfo().Assembly
let assemblyName = serviceAssembly.GetName().Name
let version = serviceAssembly.GetName().Version.ToString()
select new { service, assemblyName, version };
However, recently I added a new kind of "restartable" communication service that doesn't handle any communication itself but delegates to other services. 但是,最近,我添加了一种新型的“可重新启动”通信服务,该服务本身不处理任何通信,而是委托给其他服务。 This makes it unsuitable as a candidate for testing.
这使其不适合作为测试的候选人。 However, since it's a public concrete type, Autofac tries (and fails) to resolve it:
但是,由于它是公共的具体类型,因此Autofac尝试(但失败)解决它:
DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyCompany.Communications.UsbDevice.RestartableUsbCommunicationService' can be invoked with the available services and parameters: Cannot resolve parameter 'Int32 vendorId' of constructor 'Void .ctor(Int32, Int32, DelegateServiceFactory, LibUsbDotNet.DeviceNotify.IDeviceNotifier, Serilog.ILogger)'.
DependencyResolutionException:找不到带有类型'MyCompany.Communications.UsbDevice.RestartableUsbCommunicationService'的'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'的构造函数,无法使用可用的服务和参数调用:无法解析构造函数'的参数'Int32 vendorId'无效.ctor(Int32,Int32,DelegateServiceFactory,LibUsbDotNet.DeviceNotify.IDeviceNotifier,Serilog.ILogger)'。
I make the assumption that any service that can be resolved is one I want to test (one directly involved with communication with a device), whereas ones that fail to resolve are types like this, that just wrap other services. 我假设可以解析的任何服务都是我要测试的服务(一个直接与设备通信有关的服务),而无法解析的服务就是这种类型,只包装其他服务。
I've fixed this by writing the method below, which iterates over every registration that exposes the desired type of service and tries to resolve them in turn. 我已经通过编写下面的方法来解决此问题,该方法将对每次公开显示所需服务类型的注册进行迭代,并尝试依次解决它们。 The ones that resolve are returned and the ones that fail to are ignored.
解析的将返回,而解析失败的将被忽略。
private static IEnumerable<T> TryResolveCollection<T>(IComponentContext context)
{
bool RegistrationProvidesService(IComponentRegistration registration) =>
registration.Services.OfType<IServiceWithType>().Any(it => it.ServiceType.Name == typeof(T).Name);
var registrations = context.ComponentRegistry.Registrations.Where(RegistrationProvidesService);
var resolvedServices = new HashSet<Type>();
foreach (var registration in registrations)
{
T service;
try
{
service = (T)context.ResolveComponent(registration, Enumerable.Empty<Parameter>());
}
catch (DependencyResolutionException)
{
continue;
}
if (resolvedServices.Add(service.GetType()))
{
yield return service;
}
}
}
This could be turned into an extension method on IComponentContext
pretty easily, but since I only need it in one place, I just defined it as a private method and use it like this: 可以很容易地将其转换为
IComponentContext
上的扩展方法,但是由于我只需要一个位置,因此我将其定义为私有方法并按如下方式使用它:
var query = from service in TryResolveCollection<ICommunicationService>(container)
let serviceType = service.GetType()
let serviceAssembly = serviceType.GetTypeInfo().Assembly
let assemblyName = serviceAssembly.GetName().Name
let version = serviceAssembly.GetName().Version.ToString()
select new { service, assemblyName, version };
What's interesting is that when I run this, it still manages to resolve a RestartableUsbCommunicationService
, even though I have no idea what will happen if I try to run that (I don't know which underlying communication service it's wrapping). 有趣的是,当我运行它时,它仍然设法解决
RestartableUsbCommunicationService
,即使我不知道如果尝试运行它也会发生什么(我不知道它包装了哪个底层通信服务)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.