C# 具有IEnumerable关系类型的Autofac键控服务

C# 具有IEnumerable关系类型的Autofac键控服务,c#,.net,dependency-injection,autofac,C#,.net,Dependency Injection,Autofac,我在应用程序中使用Autofac来管理依赖项。我有一个接口和许多接口实现。实际实现在容器中注册为键控服务 我想做的是解析每个服务(因此是IEnumerable)的一个实例,这些服务使用特定的键类型注册(因此是类型化注册) 如果我直接使用容器,它可以工作: container.ResolveKeyed<IEnumerable<IService>>(MyServiceGroups.Group1); // This returns the a list of IService

我在应用程序中使用Autofac来管理依赖项。我有一个接口和许多接口实现。实际实现在容器中注册为键控服务

我想做的是解析每个服务(因此是IEnumerable)的一个实例,这些服务使用特定的键类型注册(因此是类型化注册)

如果我直接使用容器,它可以工作:

container.ResolveKeyed<IEnumerable<IService>>(MyServiceGroups.Group1);
// This returns the a list of IService implementor objects, that were previously registered with the given key
我做错了什么?有什么办法可以让这一切顺利进行吗?我可能会将Func和IEnumerable类型组合起来,并以这种方式手动从容器中解析(因为这样做是可行的),但我希望保留这种结构

编辑 代码的具体示例:

public class SubserviceModule : Autofac.Module
{
  protected override void Load(ContainerBuilder builder)
  {

    builder.RegisterType<SubServiceA>().As<ISubService>().Keyed<ISubService>(ServiceType.TypeX);
    builder.RegisterType<SubServiceB>().As<ISubService>().Keyed<ISubService>(ServiceType.TypeX);

    builder.RegisterType<SubServiceC>().As<ISubService>().Keyed<ISubService>(ServiceType.TypeY);
    builder.RegisterType<SubServiceD>().As<ISubService>().Keyed<ISubService>(ServiceType.TypeY);
  }
}

public class ServiceModule : Autofac.Module
{
  protected override void Load(ContainerBuilder builder)
  {
     builder.RegisterType<Service1>();                     
     builder.RegisterType<Service2>();
  }
}

public abstract class ServiceBase
{
    public ServiceBase(IEnumerable<ISubService> subServices) {/*...*/}
}

public class Service1
{
   public ServiceA([KeyFilter(ServiceGroup.ServiceTypeX)] IEnumerable<ISubService> subServices) 
       : base(subServices) { /* ... */ }
}

public class Service2
{
   public ServiceB([KeyFilter(ServiceGroup.ServiceTypeY)] IEnumerable<ISubService> subServices) 
       : base(subServices) { /* ... */ }
}
公共类子服务模块:Autofac.Module
{
受保护的覆盖无效负载(ContainerBuilder builder)
{
builder.RegisterType().As().Keyed(ServiceType.TypeX);
builder.RegisterType().As().Keyed(ServiceType.TypeX);
builder.RegisterType().As().Keyed(ServiceType.TypeY);
builder.RegisterType().As().Keyed(ServiceType.TypeY);
}
}
公共类服务模块:自动传真模块
{
受保护的覆盖无效负载(ContainerBuilder builder)
{
RegisterType();
RegisterType();
}
}
公共抽象类ServiceBase
{
公共服务库(IEnumerable子服务){/*…*/}
}
公共类服务1
{
公共服务A([KeyFilter(ServiceGroup.ServiceTypeX)]IEnumerable子服务)
:基本(子服务){/*…*/}
}
公共类服务2
{
公共服务B([KeyFilter(ServiceGroup.ServiceTypeY)]IEnumerable子服务)
:基本(子服务){/*…*/}
}

嗯,现在我自己想出了一个答案。我基本上做了我期望Autofac会做的事情:遍历所有参数,并使用当前解析上下文来解析它。我自己检查属性的参数,如果它在那里,我将作为键控服务解析,否则,我只解析。我还创建了一个很好的小扩展方法来隐藏此注册增加的复杂性:

public static class AutofacExtensions
{
    public static IRegistrationBuilder<TService, SimpleActivatorData, SingleRegistrationStyle> RegisterModulePageViewModel<TService>(this ContainerBuilder builder) where TService : ServiceBase
    {
        return builder.Register(ctx => CreateInstance<TService>(ctx));
    }

    private static TService CreateInstance<TService>(IComponentContext ctx)
    {
        var ctor = typeof(TService).GetConstructors().Single();
        List<object> parameters = new List<object>();
        foreach (var param in ctor.GetParameters())
        {
            var keyAttribute = param.GetCustomAttribute<KeyFilterAttribute>();
            if (keyAttribute != null)
            {
                parameters.Add(ctx.ResolveKeyed(keyAttribute.Key, param.ParameterType));
            }
            else
            {
                parameters.Add(ctx.Resolve(param.ParameterType));
            }
        }
        return (TService)ctor.Invoke(parameters.ToArray());
    }
}
公共静态类AutofacExtensions
{
公共静态IRegistrationBuilder RegisterModulePageViewModel(此ContainerBuilder生成器),其中TService:ServiceBase
{
返回builder.Register(ctx=>CreateInstance(ctx));
}
专用静态TService CreateInstance(IComponentContext ctx)
{
var ctor=typeof(TService).GetConstructors().Single();
列表参数=新列表();
foreach(ctor.GetParameters()中的var param)
{
var keyAttribute=param.GetCustomAttribute();
if(keyAttribute!=null)
{
parameters.Add(ctx.resolvedkeyed(keyAttribute.Key,param.ParameterType));
}
其他的
{
parameters.Add(ctx.Resolve(param.ParameterType));
}
}
return(TService)ctor.Invoke(parameters.ToArray());
}
}

您能展示一下您是如何向Autofac注册的吗?@ovation22用模块和服务定义编辑了这个问题。请参阅。您没有DI问题,您有一个可以通过设计模式解决的应用程序设计问题。构建一个可以基于一个键选择多个类的系统非常简单,依靠DI容器来实现这一点意味着你的应用程序与DI容器紧密耦合。@NightOwl888谢谢你的评论。您的方法和我的方法的区别在于,您有一个从所有类型的实例中选择的组件。我想要的是只接收选定的(因此不实例化服务不需要的)。至于紧耦合,我看到的唯一问题是属性本身来自Autofac。但是,如果我自己编写,我可以手动对任何容器进行类似的解析(就像我在要点中对Autofac所做的那样)。此外,DI容器对我来说是一个稳定的依赖项,因此耦合问题较少。它可以选择多种类型。您只需更改每个类型的
AppliesTo
实现,这样就可以通过一个键选择多个类型。如果要延迟加载类型,可以。
public static class AutofacExtensions
{
    public static IRegistrationBuilder<TService, SimpleActivatorData, SingleRegistrationStyle> RegisterModulePageViewModel<TService>(this ContainerBuilder builder) where TService : ServiceBase
    {
        return builder.Register(ctx => CreateInstance<TService>(ctx));
    }

    private static TService CreateInstance<TService>(IComponentContext ctx)
    {
        var ctor = typeof(TService).GetConstructors().Single();
        List<object> parameters = new List<object>();
        foreach (var param in ctor.GetParameters())
        {
            var keyAttribute = param.GetCustomAttribute<KeyFilterAttribute>();
            if (keyAttribute != null)
            {
                parameters.Add(ctx.ResolveKeyed(keyAttribute.Key, param.ParameterType));
            }
            else
            {
                parameters.Add(ctx.Resolve(param.ParameterType));
            }
        }
        return (TService)ctor.Invoke(parameters.ToArray());
    }
}