2010-12-08 44 views
1

J'écris un résolveur de sous-dépendance pour castle windsor. Le résolveur renvoie un objet qui implémente une interface générique. Les paramètres génériques sont résolus lors de l'exécution et factory est utilisé pour renvoyer l'implémentation correcte. Je ne veux pas utiliser une chaîne pour obtenir le MethodInfo de la méthode d'usine. Les travaux suivants, mais je pense qu'il doit y avoir une meilleure façon de résoudre la méthode de création d'usine, voir GetMethodName et comment il est utilisé.En utilisant la réflexion pour obtenir MethodInfo générique sans utiliser un nom de chaîne et les paramètres génériques sont résolus à l'exécution

public class FooFactoryResolver : ISubDependencyResolver 
{ 
    private static string factoryMethodName; 
    private readonly IWindsorContainer container; 

    public FooFactoryResolver (IWindsorContainer container) 
    { 
     this.container = container; 
    } 

    private static string GetMethodName() 
    { 
     if (factoryMethodName == null) 
     { 
      IFooFactory fooFactory = null; 

      Expression<Func<IFoo<object, object>>> expression = 
       () => fooFactory .CreateFoo<object, object>(); 

      factoryMethodName = ((MethodCallExpression)expression.Body). 
       Method.Name; 
     } 
     return factoryMethodName; 
    } 

    public object Resolve(CreationContext context, 
     ISubDependencyResolver contextHandlerResolver, 
     Castle.Core.ComponentModel model, DependencyModel dependency) 
    { 
     return 
      TryToResolveDirectly(dependency) ?? 
      TryToResolveUsingFactories(dependency) ?? 
      ComponentNotFound(dependency); 
    } 

    private static object ComponentNotFound(DependencyModel dependency) 
    { 
     throw new ComponentNotFoundException(dependency.TargetType); 
    } 

    private object TryToResolveUsingFactories(DependencyModel dependency) 
    { 
     var fooFactories = this.container.ResolveAll<IFooFactory>(); 

     Type[] genericTypes = dependency.TargetItemType. 
      GetGenericArguments().ToArray(); 

     return (from fooFactory in fooFactories 
       where fooFactory.CanCreate(genericTypes[0], 
        genericTypes[1]) 
       let factoryMethod = fooFactory.GetType(). 
        GetMethod(GetMethodName()) 
       select factoryMethod.MakeGenericMethod( 
        genericTypes.ToArray()). 
        Invoke(fooFactory, new object[0])). 
        FirstOrDefault(); 
    } 

    private object TryToResolveDirectly(DependencyModel dependency) 
    { 
     return this.container.Kernel.HasComponent(dependency.TargetType) ? 
      this.container.Resolve(dependency.TargetType) : null; 
    } 

    public bool CanResolve(CreationContext context, 
     ISubDependencyResolver contextHandlerResolver, 
     Castle.Core.ComponentModel model, DependencyModel dependency) 
    { 
     return dependency.TargetType.GetGenericTypeDefinition() == 
      typeof(IFoo<,>); 
    } 
} 

public interface IFoo<T1, T2> { } 

public interface IFooFactory 
{ 
    IFoo<T1, T2> CreateFoo<T1, T2>(); 
    bool CanCreate(Type a, Type b); 
} 

Je ne suis pas sûr que ce soit un abus ou non, mais il fait le travail, je me sens que je manque quelque chose d'évident. J'espérais qu'il y aurait un moyen de changer les paramètres génériques sur MethodInfo à partir de MethodCallExpression ou un moyen de revenir de MethodInfo à son 'Parent' et d'appeler MakeGenericMethod en utilisant les types que je veux.

+2

Prise en charge des génériques ouverts dans l'API Reflection. C'est un fait triste. Si cela fonctionne, laissez-le tel quel et passez à des tâches plus intéressantes. Je ne passerais pas trop de temps là-dessus. –

+0

@Krzysztof C'est bien si vous n'êtes pas OCD :). Merci pour l'appel de réveil. – Bronumski

Répondre

0

Créez un attribut personnalisé que vous appliquez à la méthode CreateFoo, puis utilisez Reflection pour rechercher cet attribut sur le type d'interface (vous le trouverez toujours). Cela évite d'avoir à créer l'expression et vous pouvez simplement rechercher votre attribut. Une fois que vous avez trouvé le MethodInfo activé, vous pouvez obtenir le nom à partir de là.

Il n'est pas nécessaire d'obtenir une déclaration de type fermé pour IFooFactory.CreateFoo<T1, T2>(); vous essayez simplement d'obtenir le nom, la fermeture des déclarations de méthode avec les types ne change pas le nom.