2009-09-14 13 views
10

Tenemos una aplicación que se integra opcionalmente con TFS, sin embargo, como la integración es opcional obviamente no quiero tener todas las máquinas necesitan los ensamblajes TFS como requisito.Tratando con dependencias opcionales (C#)

¿Qué debo hacer?

  1. ¿Está bien para mí para hacer referencia a las bibliotecas de TFS en mis conjuntos principales y sólo asegúrese de que sólo hacer referencia a objetos relacionados TFS cuando estoy usando la integración de TFS.
  2. Como alternativa, la opción más segura sería la de hacer referencia a las bibliotecas de TFS en un poco de montaje separada "TFSWrapper":

    a. ¿Está bien entonces para mí hacer referencia directamente a ese ensamblaje (de nuevo, siempre que tenga cuidado con lo que llamo)

    b. Debo, en cambio, exponer un conjunto de interfaces para que implementen mi ensamblaje TFSWrapper, y luego instanciar esos objetos usando reflexión cuando sea necesario.

1 parece arriesgado para mí, en la otra cara 2b parece over-the-top - que en esencia sería la construcción de un sistema de plug-in.

Seguramente debe haber una manera más simple.

+0

"TFS" en este caso es "Team Foundation System", ¿verdad? –

+0

Sí, pero considero que la pregunta es menos sobre TFS y más con la mejor manera de hacer referencia a ensamblajes que pueden no estar presentes en algunas máquinas de usuarios. – Justin

+0

D'oh, no leí la primera oración con cuidado, así que no me di cuenta de que con 'opcional' querías decir 'puede que no esté presente en la máquina del usuario'. –

Respuesta

6

La forma más segura (es decir, la manera más fácil de no cometer un error en su aplicación) podría ser la siguiente.

Hacer una interfaz que abstrae el uso de TFS, por ejemplo:

interface ITfs 
{ 
    bool checkout(string filename); 
} 

Escriba una clase que implementa esta interfaz utilizando TFS:

class Tfs : ITfs 
{ 
    public bool checkout(string filename) 
    { 
    ... code here which uses the TFS assembly ... 
    } 
} 

escribir otra clase que implementa esta interfaz sin necesidad de utilizar TFS:

class NoTfs : ITfs 
{ 
    public bool checkout(string filename) 
    { 
    //TFS not installed so checking out is impossible 
    return false; 
    } 
} 

Tiene un singleton en alguna parte:

static class TfsFactory 
{ 
    public static ITfs instance; 

    static TfsFactory() 
    { 
    ... code here to set the instance 
    either to an instance of the Tfs class 
    or to an instance of the NoTfs class ... 
    } 
} 

Ahora sólo hay un lugar que tiene que tener cuidado (es decir, el constructor TfsFactory); el resto de su código puede invocar los métodos ITfs de su TfsFactory.instance sin saber si TFS está instalado.


Para responder a los comentarios recientes indican a continuación:

Según mis pruebas (no sé si este es el comportamiento definido ') se produce una excepción cuando (tan pronto como sea) se llama a un método que depende del ensamblaje que falta. Por lo tanto, es importante encapsular su ensamblado code-which-depends-on-the-missing en al menos un método separado (o una clase separada) en su ensamblaje.

Por ejemplo, lo siguiente no se cargará si el ensamblaje de conversación es que falta:

using System; 
using OptionalLibrary; 

namespace TestReferences 
{ 
    class MainClass 
    { 
     public static void Main(string[] args) 
     { 
      if (args.Length > 0 && args[0] == "1") { 
       Talk talk = new Talk(); 
       Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!"); 
      } else { 
       Console.WriteLine("2 Hello World!"); 
      } 
     } 
    } 
} 

la siguiente carga voluntad:

using System; 
using OptionalLibrary; 

namespace TestReferences 
{ 
    class MainClass 
    { 
     public static void Main(string[] args) 
     { 
      if (args.Length > 0 && args[0] == "1") { 
       foo(); 
      } else { 
       Console.WriteLine("2 Hello World!"); 
      } 
     } 

     static void foo() 
     { 
      Talk talk = new Talk(); 
      Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!"); 
     } 
    } 
} 

Estos son los resultados de la prueba (utilizando MSVC# 2010 y .NET en Windows):

C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe 
2 Hello World! 

C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe 1 

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'OptionalLibrary, Version=1.0.0.0, 
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. 
    at TestReferences.MainClass.foo() 
    at TestReferences.MainClass.Main(String[] args) in C:\github\TestReferences\TestReferences\TestReferences\Program.cs: 
line 11 

C:\github\TestReferences\TestReferences\TestReferences\bin\Debug> 
+0

¿Pero estás diciendo que está bien si la implementación de la clase Tfs vive dentro del mismo ensamblaje? – Justin

+0

Usando la API tradicional de Windows, si enlaza a una DLL que no existe en la máquina del usuario final, entonces su ejecutable no se cargará en absoluto: por lo tanto, necesita usar LoadLibrary explícita. No estoy seguro de haber probado esto, pero no creo que eso sea cierto más de referencias dotNet a ensamblajes que no existen en la máquina del usuario final ... y entonces es seguro tener esas referencias en su ensamblado (habrá una excepción de tiempo de ejecución solo cuando/si intentas llamar al ensamblaje inexistente). – ChrisW

+0

Eso haría que TfsFactory dependa del ensamblaje que contenga la clase Tfs. Y dado que TfsFactory se utiliza en la aplicación, aún así será necesario distribuir el software con el dll Tfs "opcional". :-) –

0

Un concepto de "complemento" puede ser el camino a seguir, y también puede permitir (más adelante) ampliar su aplicación para que funcione con otros productos que no sean TFS si es necesario. La opción 2a será igual de "arriesgada" (fallar cuando falten los archivos vinculados) como opción 1.

Puede hacer un ensamblaje con las interfaces requeridas para su propósito específico, y hacer referencia a este ensamblaje tanto desde su aplicación como desde "TFS plug-in". Este último luego proporciona implementaciones de sus interfaces y usa TFS para realizar las operaciones. La aplicación puede cargar dinámicamente un ensamblaje y crear instancias de los tipos de plug-in necesarios (a través deetc.) y convertir esas instancias a sus interfaces.

De hecho, si haces que esos tipos hereden de MarshalByRef, incluso puedes cargarlos en otro AppDomain y así hacer una separación limpia de tus complementos, y también hacerlos descargables.

+0

En realidad estoy tratando de implementar un sistema de complemento. Sin embargo, a veces me gustaría usar un tipo introducido en otro ensamblaje. El código que usa este tipo estará protegido con una declaración "if" que garantizará que solo se use cuando la biblioteca esté disponible y cargada. ¿Hay alguna manera de hacer esto en C#? –

+0

@SergiyByelozyorov, sí, esto se puede hacer, pero solo a través de la vinculación tardía (por ejemplo, Reflexión o dinámica). Si el tipo puede implementar alguna interfaz que esté siempre disponible, será más cómodo de usar. – Lucero

1

Puede consultar Managed Extensibility Framework (MEF).

+0

¡Eso se ve bastante interesante, aunque posiblemente un poco exagerado por lo que estoy haciendo en este momento! Probablemente lo eche un vistazo más tarde. – Justin

Cuestiones relacionadas