2010-09-16 4 views
2

Я проектирую слабо связанную структуру. Я хочу, чтобы вызывал классы из разных сборок/пространств имен через код, который представлен строкой. Мой дизайн заключается в том, что каждый из бизнес-правил клиента находится на разных собраниях и не зависит друг от друга (ОДИН клиент относится к ОДНОМ DLL-соотношению), поэтому, когда я сделал обновление бизнес-правил 1 клиента, это не повлияет на других. Теперь я обратил внимание на использование Factory Design и использование метода Activator.CreateInstance().Activator.CreateInstance: динамическое создание объектов

Это проект установки (2 + п библиотек)

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 

Реальный код

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();  
    } 

Любая идея, почему мой пример не работает? И любое предложение по улучшению дизайна? Спасибо заранее.

Как об использовании

Expression.Lambda

кого?

ответ

2

Благодаря вашей помощи, ребята, я, наконец, понял!Я просто изменить завод

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); 

    } 
} 

Но интересно о его ЭФФЕКТИВНОСТЬ (производительность удара), так как он использует отражение ..

1

Вам необходимо использовать полное имя класса.

, например:

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

Если вы используете FetchInstance ("Client.BusinessRules") ваш код работает, если все в той же сборке. Если это не так (согласно вашему дизайну), вам нужно дать AssemblyQualifiedName.

Я бы сделал дизайн по-другому, хотя. Держите свой вызов только с «Client1» в качестве параметра, но измените реализацию Factory. Динамически загружайте сборку для данного клиента (с помощью Assembly.Load() или Assembly.LoadFrom()), а затем используйте clientAssembly.CreateInstance(), чтобы указать ваш тип.

Edit: Сырая Пример кода:

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"); 
    } 
    } 
} 

Если вы dont't знаете имя класса в клиентской библиотеки DLL, вы должны искать применимого типа, например, с clientAssembly.GetTypes().

+0

Да, я хочу, чтобы сохранить параметр как есть. – CSharpNoob

+0

Он по-прежнему выдает ошибку при передаче «Client1» по параметру с помощью вашего кода. – CSharpNoob

+0

. Вам необходимо иметь Client1.dll в том же каталоге, что и исполняющая сборка. Если это не работает: что такое сообщение об ошибке и на какой строке оно выбрано? – TToni

0

Если вы загружаете этот тип с внешней сборки, я бы рекомендовал использовать Activator.CreateInstanceFrom.

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

Я попробовал это, но он возвращает nullreferenceexception – CSharpNoob

+0

Я предполагаю, что он выбрасывает исключение nullreference из вызова Unwrap(), который говорит мне, что либо ваш buildPath, либо ваше полное QualifiedClassname неверны в вызове CreateInstanceFrom. Убедитесь, что ваш путь прав, и что вы включаете ВСЕ из своих пространств имен в ваше полное QualifiedClassname. –

0

Если вы хотите, чтобы иметь возможность добавлять бизнес-правила, как библиотеки после развертывания и создать их во время выполнения, я предлагаю вам иметь папку бизнес-правила под вас приложение, загрузите все библиотеки DLL в этом приложении, поиск всех типов что внедрение IBusinessRules в каждой dll с использованием рефлексии. Учитывая, что теперь у вас есть дескрипторы типов, создание одного, основанного на имени, было бы легким, и ваш проект уменьшился бы.

Либо это, либо передать квалифицированное имя класса классам вашим методам.

Смежные вопросы