2010-09-16 18 views
2

Je suis en train de concevoir une structure faiblement couplée. Je veux appeler des classes de différents assemblys/namespaces via un code qui est représenté par une chaîne. Ma conception est que chacune des règles métier du client est sur des assemblages différents et ne dépend pas l'un de l'autre (un client est à un ratio DLL) de sorte que lorsque je fais une mise à jour sur les règles métier de 1 client, cela n'affecte pas les autres. Mon attention est maintenant sur l'utilisation de Factory Design et en utilisant Activator.CreateInstance() Méthode.Activator.CreateInstance: Instanciation dynamique des classes

Ceci est la configuration du projet (2 + n DLL)

namespace Foundation; // where the interfaces/abstract resides 
namespace Factory; // has dependency on Foundation assembly 
namespace Client1; // client1's DLL, no dependency 
namespace Client2; // client2's DLL, no dependency 
The UI // only referenced to the Foundation and Factory not the Clients 

Le code réel

namespace Foundation 
{ 
    public interface IBusinessRules 
    { 
    string GetBusinessRule(); 
    } 
} 

namespace Client1 //DLL for client 1 
{ 
    public class BusinessRules : Foundation.IBusinessRules 
    { 
    public string GetBusinessRule() 
    { 
     return "Client1 Business Rule"; 
    } 
    } 
} 

namespace Client2 //DLL for client 2 
{ 
    public class BusinessRules : Foundation.IBusinessRules 
    { 
    public string GetBusinessRule() 
    { 
     return "Client2 Business Rule"; 
    } 
    } 
} 


namespace Factory 
{ 
    public static class Invoker<T> where T: Foundation.IBusinessRules 
    { 
    public static T FetchInstance(string clientCode) 
    { 
     return (T)Activator.CreateInstance(Type.GetType(clientCode)); 
    } 
    } 
} 


//sample implementation that generates unhandled Exception 
using Factory; 
using Foundation; 
static void Main(string[] args) 
{ 
     //the parameter is maintained in the database 
     IBusinessRules objClient1 = Invoker<IBusinessRules>.FetchInstance("Client1"); 

     //should call Client1.BusinessRules method 
     Console.WriteLine(objClient.GetBusinessRule()); 
     Console.Read(); 

     objClient = Invoker<IBusinessRules>.FetchInstance("Client2"); 

     //should call Client2.BusinessRules method 
     Console.WriteLine(objClient.GetBusinessRule()); 
     Console.Read();  
    } 

Toute idée pourquoi mon exemple ne fonctionne pas? Et toute suggestion pour améliorer la conception? Merci d'avance.

Comment l'utilisation

Expression.Lambda

quelqu'un?

Répondre

2

Grâce à votre aide, les gars, j'ai enfin réussi!Je l'usine juste modifier

namespace Factory 
{ 
    public static class Invoker<T> where T : Foundation.IBusinessRules 
    { 
     public static T FetchInstance(string clientCode) 
     { 
      Type objType = Type.GetType(clientCode + ".BusinessRules," + clientCode); 
      return (T)Activator.CreateInstance(objType); 

    } 
} 

Mais je me demande au sujet de son effeciency (coup de performance), car il utilise la réflexion ..

1

Vous devez utiliser le nom complet de la classe.

par exemple:

Type.GetType("System.Collections.Generic.Dictionary`2[System.String,[MyType,MyAssembly]]") 
3

Si vous utilisez FetchInstance ("Client.BusinessRules") votre code fonctionne, si tout est dans le même ensemble. Si ce n'est pas (selon votre conception), vous devez donner un AssemblyQualifiedName.

Je ferais la conception différemment si. Gardez votre appel avec juste "Client1" comme paramètre mais changez l'implémentation de l'usine. Chargez dynamiquement l'assembly pour le client donné (avec Assembly.Load() ou Assembly.LoadFrom()), puis utilisez clientAssembly.CreateInstance() pour istancer votre type.

Edit: exemple de code brut:

namespace Factory 
{ 
    public static class Invoker<T> where T: IBusinessRules 
    { 
    public static T FetchInstance(string clientCode) 
    { 
     var clientAssembly = Assembly.LoadFrom(clientCode + ".dll"); 

     return (T)clientAssembly.CreateInstance(clientCode+".BusinessRules"); 
    } 
    } 
} 

Si vous Dont't connais le nom de classe dans la dll client, vous devez rechercher un type applicable, par exemple avec clientAssembly.GetTypes().

+0

Oui, je veux garder le paramètre comme il est. – CSharpNoob

+0

Il jette toujours une erreur lorsque je passe "Client1" sur le paramètre en utilisant votre code – CSharpNoob

+0

Vous devez avoir le Client1.dll dans le même répertoire que l'assembly en cours d'exécution. Si cela ne fonctionne pas: Quel est le message d'erreur et à quelle ligne est-il lancé? – TToni

0

Si vous chargez le type à partir d'un assemblage externe, je vous recommande d'utiliser Activator.CreateInstanceFrom.

var typeReference = Activator.CreateInstanceFrom(assemblyPath, fullyQualifiedClassName); 
return typeReference.Unwrap() as T; 
+0

Je l'ai essayé mais il renvoie nullreferenceexception – CSharpNoob

+0

Je suppose qu'il lance l'exception nullreference de l'appel Unwrap(), qui me dit que votre assemblyPath ou votre nomQualifiedClassname sont incorrects dans l'appel CreateInstanceFrom. Assurez-vous que votre chemin d'accès est correct et que vous incluez TOUS vos espaces de noms dans votre nom de classe fullyQualified. –

0

Si vous voulez être en mesure d'ajouter des règles métier comme dll après le déploiement et les créer à l'exécution, je vous suggère d'avoir un dossier de règles métier sous vous application, chargez tous dll dans cette application, rechercher tous les types cette implémentation de IBusinessRules dans chaque DLL en utilisant la réflexion. Étant donné que vous avez maintenant des handles sur les types, en créer un basé sur le nom serait facile et votre projet serait à l'échelle.

Soit cela, soit de passer le nom qualifié de l'ensemble des classes à vos méthodes.