2011-06-13 7 views
6

Estoy tratando de invocar el método RegisterType en el contenedor de Unity. RegisterType tiene un total de 16 reemplazos (algunos de esos son parámetros, algunos son tipos).Invocando un método a través de la reflexión con genéricos e invalida

Estoy tratando de realizar el equivalente de:

Container.RegisterType<IMyDataProvider, MockData.MockProvider>("MockData", new ContainerControlledLifetimeManager()) 

Usando GetMethod() fue un fracaso total, por lo que terminó haciendo esta cosa fea:

 MethodInfo registerTypeGeneric = Container.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance). 
     Where(p => p.ToString() == "Microsoft.Practices.Unity.IUnityContainer RegisterType[TFrom,TTo](System.String, Microsoft.Practices.Unity.LifetimeManager, Microsoft.Practices.Unity.InjectionMember[])").FirstOrDefault(); 
    MethodInfo registerTypeSpecific = registerTypeGeneric.MakeGenericMethod(new Type[] { typeof(IMyDataProvider), Assembly.LoadFrom("MockData.dll").GetType("MockData.MockProvider") }); 
    registerTypeSpecific.Invoke(Container, new object[] { "MockData", new ContainerControlledLifetimeManager() }); 

y esto funciona muy bien , hasta la invocación que se queja porque no tengo parámetros InjectionMember (son opcionales y no tengo ninguno para dar). Entonces, según la documentación, tengo que usar Type.InvokeMember() para llamar a un método con parámetros opcionales.

Así lo hice:

 Binder binder = new BootstrapperBinder(); 
    Container.GetType().InvokeMember("RegisterType", 
     BindingFlags.Instance | BindingFlags.Public | BindingFlags.OptionalParamBinding | BindingFlags.InvokeMethod, 
     binder, 
     Container, 
     new object[] { "MockData", new ContainerControlledLifetimeManager() }); 

Mi clase BoostrapperBinder hace esto:

public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] names, out object state) 
    { 
    Type mockProvider = Assembly.LoadFrom("MockData.dll").GetType("MockData.MockProvider"); 
    state = new object(); 
    MethodInfo mi = Container.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance). 
     Where(p => p.ToString() == "Microsoft.Practices.Unity.IUnityContainer RegisterType[TFrom,TTo](System.String, Microsoft.Practices.Unity.LifetimeManager, Microsoft.Practices.Unity.InjectionMember[])").FirstOrDefault(); 
    return mi.MakeGenericMethod(new Type[] { typeof(ICarrierApprovalDataChangeAccessorEndPoint), mockProvider }); 
    } 

Sí, es feo, pero yo sólo lo utilizan para este caso oen, por lo que hace el trabajo.

Ahora, el problema es que sigue quejándose de la falta de un tercer parámetro. No puedo pasar null o Missing.Value tampoco, o croa. Lo he intentado con y sin BindingFlags.OptionalParamBinding. Estoy perplejo.

(editado para poner el ejemplo Container.RegisterType en código)

Respuesta

1

Mi publicación inicial mencionaba que había intentado pasar null como un tercer parámetro y que la aplicación "graznaba". Específicamente, obtenía una excepción de referencia nula y debería haber sido más claro al respecto.

La solución era pasar "nueva InjectionMember [0]" en lugar de null, por lo que la invocación() debería haber tenido este aspecto:

registerTypeSpecific.Invoke(Container, new object[] { "MockData", new ContainerControlledLifetimeManager(), new InjectionMember[0] }); 

Gracias a Jason por su ayuda. Su muestra me envió por el camino que finalmente condujo a la respuesta.

1

I no puede pasar null o Missing.Value ya sea, o se estira la pata.

Croaks how? Usted debe ser capaz de pasar null para un parámetro params (cuando se invoca un método como M(params object[] objects) través M() será el caso de que objects es nulo dentro del método.

En segundo lugar, se puede buscar el método más limpia. no tengo un compilador a mi alcance, pero trata de esto:

var registerTypeMethodInfo = 
    typeof(IUnityContainer).GetMethods() 
          .Where(m => m.Name == "RegisterType") 
          .Where(m => m.GetParameters() 
           .Select(p => p.ParameterType) 
           .SequenceEqual(new[] { 
             typeof(string), 
             typeof(LifetimeManager), 
             typeof(InjectionMember[]) 
           }) 
          ) 
          .Where(m => m.GetGenericArguments().Count() == 2) 
          .SingleOrDefault(); 
Assert.NotNull(registerTypeMethodInfo); 
var methodInfo = 
    registerTypeMethodInfo.MakeGenericMethod(new[] { 
     typeof(IMyDataProvider), 
     typeof(MockData.MockProvider) 
    }); 

continuación, se invoca el método como tal, pasando null para el parámetro params InjectionMember[]:

methodInfo.Invoke(
    Container, 
    new object[] { 
     "MockData", 
     new ContainerControlledLifetimeManager(), 
     null 
    } 
); 

Lo siento si no se compila. Si no se compila, esto te acercará mucho a la solución correcta.

Aquí está un ejemplo autónoma que funciona:

namespace ParamsTest { 
    interface Foo { 
     void M<T>(string s, int n, params object[] objects); 
    } 
    class Bar : Foo { 
     public void M<T>(string s, int n, params object[] objects) { 
      Console.WriteLine(s); 
      Console.WriteLine(n); 
      Console.WriteLine(objects == null); 
      Console.WriteLine(typeof(T).Name); 
     } 
    } 
    internal class Program { 
     internal static void Main(string[] args) { 
      var genericMethodInfo = 
       typeof(Foo).GetMethods() 
        .Where(m => m.Name == "M") 
        .Where(m => m.GetParameters() 
         .Select(p => p.ParameterType) 
         .SequenceEqual(new[] { 
          typeof(string), 
          typeof(int), 
          typeof(object[]) 
         }) 
        ) 
        .Where(m => m.GetGenericArguments().Count() == 1) 
        .SingleOrDefault(); 
      var methodInfo = 
       genericMethodInfo.MakeGenericMethod(
        new[] { typeof(DateTime) } 
       ); 
      var bar = new Bar(); 
      methodInfo.Invoke(bar, new object[] { "Hello, world!", 17, null }); 
     } 
    } 
} 

Esta impresora:

Hello, world! 
17 
True 
DateTime 

en la consola.

He intentado con y sin BindingFlags.OptionalParamBinding.Estoy perplejo.

params no es parte de la firma de un método. Es un truco de compilación para permitir listas de parámetros de longitud variable. BindingFlags.OptionalParamBinding es para enlazar parámetros opcionales a sus valores predeterminados.

+0

El LINQ en GetMethods falla debido a más de un retorno (tiene 16 anulaciones, algunas tienen diferentes parámetros de tipo genérico), pero si utilizo la forma de obtener la información del método, la invocación aún falla con un nulo excepción de referencia en el método RegisterType(). Como el contenedor no es nulo, solo puedo suponer que el parámetro nulo es la causa. – Pete

+0

Eso es fácil de arreglar. Agregue un '.Where (m => m.GetGenericArguments(). Count() == 2)' antes de la llamada a 'SingleOrDefault'. – jason

+0

En cuanto al problema que experimenta con 'null', intente invocar el método a través de una invocación de método estático (es decir, elimine la reflexión). Simplemente llame a 'Container.RegisterType (" MockData ", nuevo ContainerControlledLifetimeManager(), null)' y vea si obtiene el mismo problema. Si lo haces, el problema NO es la invocación dinámica y, en cambio, es otra cosa (¿quizás la forma en que configuraste el contenedor?). – jason

Cuestiones relacionadas