【问题标题】:Using Autofac, how can I resolve a collection of services and ignore ones that fail to resolve?使用 Autofac,我如何解决一组服务并忽略无法解决的服务?
【发布时间】:2018-02-09 08:40:26
【问题描述】:

总结

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

详情

在我的一个库中,我定义了一个ICommunicationService 接口,我在其他各个地方实现了该接口。我创建了一个“测试平台”ASP.NET Core 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' 可以使用可用的服务和参数调用:不能 解析构造函数'Void .ctor(Int32, Int32,委托服务工厂, LibUsbDotNet.DeviceNotify.IDeviceNotifier, Serilog.ILogger)'。

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

【问题讨论】:

  • 如果不了解成功与不成功解析的构造函数签名,就很难说。但对我来说,修复你的容器配置似乎比试图找出如何阻止它以某种方式解决它更有意义。
  • @NightOwl888 “成功解决”我只是指不会导致DependencyResolutionException 被抛出的解决。我不确定这是容器配置的问题 - 问题是对于这个特定的应用程序,我要测试的唯一服务是“自包含”的服务,Autofac 可以解决它们而无需我通过自定义参数等

标签: c# dependency-injection autofac


【解决方案1】:

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

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,即使我不知道如果我尝试运行它会发生什么(我不知道它包装了哪个底层通信服务) .

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多