Activator.CreateInstance: динамическое создание классов - PullRequest
2 голосов
/ 16 сентября 2010

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

Это настройка проекта (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

Фактический код

 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

любой

Ответы [ 5 ]

3 голосов
/ 16 сентября 2010

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

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

Редактировать: Пример неочищенного кода:

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

Если вы не знаете имя класса в client-dll, вам придется искать подходящий тип, например, с помощью clientAssembly.GetTypes ().

2 голосов
/ 16 сентября 2010

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

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

    }
}

Но меня интересует ее эффективность (снижение производительности), потому что она использует Reflection ..

1 голос
/ 16 сентября 2010

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

например:

Type.GetType("System.Collections.Generic.Dictionary`2[System.String,[MyType,MyAssembly]]")
0 голосов
/ 16 сентября 2010

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

Либо так, либо передайте полное имя сборки в ваши методы.

0 голосов
/ 16 сентября 2010

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

var typeReference = Activator.CreateInstanceFrom(assemblyPath, fullyQualifiedClassName);
return typeReference.Unwrap() as T;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...