使用Autofac,如何解析一组服务并忽略无法解决的服务?

时间:2018-02-09 08:40:27

标签: c# dependency-injection autofac

摘要

我正在尝试查看是否有一种方法可以让Autofac解析特定类型的所有实现(IEnumerable<TService>),返回所有可以成功解析的实现,同时默默地忽略那些失败的实现

详细

在我的一个库中,我定义了一个ICommunicationService接口,我在其他各个地方实现了该接口。我已经创建了一个“测试床”ASP.NET核心Web服务,我可以用它在其依赖项中获取该接口的所有实现,并允许我测试它们。

当我尝试访问浏览器中的选择页面时,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 };

然而,最近我添加了一种新的“可重启”通信服务,该服务不处理任何通信本身,而是委托其他服务。这使得它不适合作为测试候选者。但是,由于它是公共具体类型,Autofac会尝试(并且失败)解决它:

  

DependencyResolutionException:找不到任何构造函数   类型上的'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'   'MyCompany.Communications.UsbDevice.RestartableUsbCommunicationService'   可以使用可用的服务和参数调用:不能   解析参数'Int32 vendorId'的构造函数'Void .ctor(Int32,   Int32,DelegateServiceFactory,   LibUsbDotNet.DeviceNotify.IDeviceNotifier,Serilog.ILogger)'。

我假设任何可以解析的服务都是我要测试的服务(一个直接涉及与设备通信的服务),而那些无法解析的服务是这样的类型,只包装其他服务。

1 个答案:

答案 0 :(得分:0)

我已经通过编写下面的方法来解决这个问题,该方法迭代每个公开所需服务类型的注册并尝试依次解决它们。将返回已解析的那些,并且将忽略那些失败的那些。

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;
        }
    }
}

这可以很容易地转换为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 };

有趣的是,当我运行它时,它仍然设法解析RestartableUsbCommunicationService,即使我不知道如果我尝试运行它会发生什么(我不知道它是哪个底层通信服务包装)。 enter image description here