2011-06-02 20 views
5

Tengo una solución .NET 2.0 Win Forms existente que se implementa en el cliente. En aras de este escenario, vamos a llamarlo 'WinApp.exe'. WinApp.exe tiene una referencia directa a un ensamblado privado llamado 'Assembly.dll'. Assembly.dll tiene una referencia directa a otro ensamblado llamado 'Framework.dll'. Dentro de Framework.dll se encuentra un tipo llamado 'IPlugIn'. Framework.dll no tiene un nombre seguro, pero la versión del ensamblado es 1.0. Hay una clase en Assembly.dll que implementa IPlugIn desde Framework.dll.Problema al crear instancias de tipo utilizando la reflexión del ensamblado de nombre seguro

Tengo otra solución .NET 2.0 Win Forms llamada 'Framework.exe'. Este proyecto de Win Forms también tiene una referencia directa a Framework.dll, que tiene definido el tipo 'IPlugIn'. Framework.dll no tiene un nombre seguro, pero la versión de este ensamblado es 2.0. Poco a poco, 'IPlugIn' en Framework.dll v2 es idéntico a 'IPlugIn' en Framework.dll v1. El código en Framework.exe utiliza la reflexión para cargar la aplicación de IPlugIn que está en WinApp:

AssemblyName an = AssemblyName.GetAssemblyName("C:\\Program Files\\WinApp\\Assembly.dll"); 
Assembly dll = Assembly.Load(an); 
object o = Activator.CreateInstance(dll.GetType("WinApp.ClassThatImplementsIPlugIn")); 
IPlugIn iPlugIn = o as IPlugIn; 

Hasta ahora, todo bien. Este código funciona! Ahora aquí está la parte problemática. Necesito asignar un nombre seguro a Framework.dll v2, para que pueda colocarse en el GAC. Sin embargo, WinApp y sus ensamblajes dependientes no se volverán a implementar; debe continuar utilizando las mismas versiones de ensamblados que está utilizando actualmente. Cuando doy Framework.dll v2 un nombre fuerte y volver a compilar y ejecutar Framework.exe, cuando se ejecuta el código anterior recibo una "InvalidCastException" en línea:

IPlugIn iPlugIn = o as IPlugIn; 

Creo que estoy recibiendo esta excepción porque ahora que una versión de Framework.dll tiene un nombre fuerte, el tiempo de ejecución trata el tipo "IPlugIn" como si fuera un tipo completamente diferente. Necesito saber si hay alguna manera de resolver este problema. De nuevo, los requisitos son:

  1. Debo poder poner Framework.dll v2 en el GAC (por lo tanto, debe tener un nombre seguro).
  2. WinApp debe continuar funcionando como lo hace actualmente (continúe haciendo referencia a Framework.dll v1). No puedo redistribuir ensamblados recién compilados para WinApp.

¡Gracias de antemano!

Chad

Respuesta

4

El problema aquí es que tanto el nombre seguro Framework como el conjunto usado Framework están cargados. A .NET realmente no le importa que ambas definiciones de IPlugin sean las mismas. Son de un ensamblaje diferente, por lo que son diferentes (estoy un poco desconcertado por el hecho de que aumentará un InvalidCastException, ya que un yeso como ese solo arrojará null si falla).

opción

Un método

Uno de utilizar todavía la clase será el uso de la reflexión, pero esto podría recorrer un largo camino cuando más tipos alojados en Framework vienen a jugar. a todos se les tendrá que acceder utilizando la reflexión. Puede crear contenedores para ocultar la reflexión de usar algo como esto:

public class PluginWrapper : IPlugin 
{ 
    object fObj; 
    PropertyInfo fNameProperty; 
    MethodInfo fGetOtherMethod; 

    public PluginWrapper(object o) 
    { 
    fObj = o; 
    fNameProperty = o.GetType().GetInterface("IPlugin").GetProperty("Name"); 
    fGetOtherMethod = o.GetType().GetInterface("IPlugin").GetMethod("GetOther", new Type[] { typeof(string) }); 
    } 

    public string Name 
    { 
    get { return (string)fNameProperty.GetValue(fObj, null); } 
    } 

    public IOther GetOther(string name) 
    { 
    object result = fGetOtherMethod.Invoke(fObj, new object[] { name }); 

    if (result == null) 
     return null; 

    return new OtherWrapper(result); 
    } 
} 

continuación, puede utilizar el objeto de la siguiente manera:

IPlugin iPlugIn = new PluginWrapper(o); 

la opción B

puedo pensar en otro camino. No estoy seguro de si este es un camino por el que deberías ir ya que se ve mucho "pirateo" en mis ojos, pero lo compartiré de todos modos y te dejaré decidir.

Puede cargar el ensamblaje utilizando una secuencia de bytes en lugar de AssemblyName. De esa manera será incapaz de resolver el otro conjunto de Framework y que le da un cambio de romper en el caso AssemblyResolve:

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
Assembly dll = Assembly.Load(File.ReadAllBytes(@"C:\Program Files\WinApp\Assembly.dll")); 
object o = Activator.CreateInstance(dll.GetType("WinApp.ClassThatImplementsIPlugIn")); 
IPlugin iPlugIn = o as IPlugin; 

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    if (args.Name.ToLower().StartsWith("framework,")) 
    return typeof(IPlugin).Assembly; 

    return null; 
} 

Esto podría ser un poco complicado, ya que por que .NET dará una promesa de que el el montaje es compatible y está preparado para correr el riesgo. Si las cosas no son compatibles, entonces los problemas pueden comenzar a surgir rápidamente.

Otras opciones

No podrían quizás ser también la manera de resolver el problema utilizando bindingredirect elemento en app.config. No soy muy conocido con los detalles sobre cómo funciona, pero podría valer la pena investigarlo un poco.

+0

"Un método para seguir utilizando la clase será utilizar la reflexión, pero esto podría ser muy útil cuando entren en juego más tipos alojados en Framework. Se necesitará acceder a todos mediante la reflexión". No estoy seguro de entender esa declaración. ¿Puedes proporcionar un código de muestra para explicar lo que eso significa? –

+0

@Chad Ernst - He agregado un código de ejemplo para usted –

+0

Peter - ¡Gracias! –

Cuestiones relacionadas