2017-10-20 17 views
3

J'utilise Autofac dans une application pour gérer les dépendances. J'ai une interface et un certain nombre d'implémentations pour l'interface. Les implémentations réelles sont enregistrées en tant que services clés dans le conteneur. Ce que je voudrais faire est de résoudre une instance de chaque service (d'où le IEnumerable) qui sont enregistrés avec un keytype spécifique (d'où l'enregistrement tapé).Service de clé Autofac avec le type de relation IEnumerable

Si j'utilise le conteneur directement, il fonctionne:

container.ResolveKeyed<IEnumerable<IService>>(MyServiceGroups.Group1); 
// This returns the a list of IService implementor objects, that were previously registered with the given key 

Cependant, si j'utilise l'attribut [KeyFilter] dans mes constructeurs pour résoudre les dépendances, il n'a pas d'effet et j'obtenir la liste de tous les services enregistrés , quelle que soit la valeur utilisée aux enregistrements avec clé.

public class MyBigService([KeyFilter(MyServiceGroups.Group1)] services) 
{ 
    // here services contains one from every type, not just the ones registered with that particular key 
} 

Qu'est-ce que je fais mal? Y a-t-il un moyen de faire fonctionner cela? Je pourrais probablement combiner les types Func et IEnumerable et résoudre le problème manuellement à partir du conteneur (puisque cela fonctionne), mais j'aimerais conserver cette structure.

EDIT Exemple concret avec le code:

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) { /* ... */ } 
} 
+0

Pouvez-vous montrer comment vous les enregistrez avec Autofac? – ovation22

+0

@ ovation22 Edité la question avec les modules et les définitions de service. –

+0

Voir [Sélection du type d'injection de dépendances] (https://stackoverflow.com/a/34331154). Vous n'avez pas de problème de DI, vous avez un problème * de conception d'application * qui peut être résolu avec des modèles de conception. Il est assez simple de construire un système qui peut sélectionner plusieurs classes en fonction d'une clé, et s'appuyer sur un conteneur DI pour le faire signifie que votre application est étroitement couplée au conteneur DI. – NightOwl888

Répondre

0

Eh bien, je suis venu avec une réponse moi-même pour l'instant. Je fais essentiellement ce que je m'attendais à faire d'Autofac: passer par tous les paramètres et utiliser le contexte de résolution actuel pour le résoudre. Je vérifie moi-même le paramètre pour l'attribut, et si c'est le cas, je le résous en tant que service avec clé, sinon, je le résous. J'ai également créé une belle petite méthode d'extension pour cacher la complexité supplémentaire de cet enregistrement:

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