简体   繁体   English

使用Autofac,如何解决服务集合并忽略无法解决的服务?

[英]Using Autofac, how can I resolve a collection of services and ignore ones that fail to resolve?

Summary 摘要

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> )的所有实现,返回可以成功解析的所有实现, 同时静默忽略失败的实现

Details 细节

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM