IoC, Dll ссылки и сканирование сборки - PullRequest
10 голосов
/ 01 мая 2009

Хотя этот вопрос относится к StructureMap, мой общий вопрос:

При подключении компонентов с IoC контейнер в коде (в отличие для настройки через xml ) у вас вообще нужен явный проект / сборка ссылки на все сборки?

Почему отдельные сборки? Потому что:


"Абстрактные классы, проживающие в отдельная сборка от их бетона реализации являются отличным способом добиться такого разделения. "- Framework Руководство по проектированию стр.91


* * 1 022 Пример: * 1 023 *

Допустим, у меня есть PersonBase.dll и Bob.dll

Bob наследуется от абстрактного класса PersonBase . Они оба в пространстве имен Person . но в разных сборках .

Я программирую на PersonBase , а не Боб .

Вернувшись в мой основной код, мне нужен человек. StructureMap может сканировать сборки. Отлично, я попрошу StructureMap для одного!

Теперь, в моем основном коде, я имею в виду только PersonBase , а не Bob . Я на самом деле не хочу, чтобы мой код знал что-нибудь о Боб . Нет ссылок на проекты, нет nuthin. Вот и весь смысл.

Итак, я хочу сказать:

//Reference: PersonBase.dll (only)
using Person;  
...

//this is as much as we'll ever be specific about Bob:
Scan( x=> { x.Assembly("Bob.dll"); }

//Ok, I should now have something that's a PersonBase (Bob). But no ?
ObjectFactory.GetAllInstances<PersonBase>().Count == 0

Не повезло. Что работает, так это то, что я хочу Боба:

//Reference: PersonBase.dll and Bob.dll
using Person; 
...
Scan( x => {x.Assembly("Bob.dll"); }

//If I'm explicit, it works. But Bob's just a PersonBase, what gives?
ObjectFactory.GetAllInstances<Bob>().Count == 1 //there he is!

Но теперь мне пришлось сослаться на Bob.dll в моем проекте, а это именно то, чего я не хотел.

Я могу избежать этой ситуации, используя конфигурацию Spring + Xml. Но потом я возвращаюсь к конфигурации Spring + Xml ...!

Я что-то упустил при использовании StructureMap или вообще принцип, делай (бегло) IoC Конфигурации нуждаются в явных ссылках на все сборки?

Возможно связанный вопрос: StructureMap и сканирующие сборки

Ответы [ 4 ]

14 голосов
/ 03 мая 2009

Я наконец разобрался с этим. Это выглядит так:

IoC Uml http://img396.imageshack.us/img396/1343/iocuml.jpg

со сборками

  • Core.exe
  • PersonBase.dll (ссылка время компиляции от Core.exe)
  • Bob.dll ( время загрузки загружено через StructureMap Scan)
  • Betty.dll ( загруженная среда выполнения через StructureMap Scan)

Чтобы получить его с StructureMap, мне потребовался специальный «ITypeScanner» для поддержки сканирования сборок:

public class MyScanner : ITypeScanner {
  public void Process(Type type, PluginGraph graph) {

    if(type.BaseType == null) return;

    if(type.BaseType.Equals(typeof(PersonBase))) {
      graph.Configure(x => 
        x.ForRequestedType<PersonBase>()
        .TheDefault.Is.OfConcreteType(type));
    }
  }
} 

Итак, мой основной код выглядит так:

ObjectFactory.Configure(x => x.Scan (
  scan =>
  {
    scan.AssembliesFromPath(Environment.CurrentDirectory 
    /*, filter=>filter.You.Could.Filter.Here*/);

    //scan.WithDefaultConventions(); //doesn't do it

    scan.With<MyScanner>();
  }
));

ObjectFactory.GetAllInstances<PersonBase>()
 .ToList()
  .ForEach(p => 
  { Console.WriteLine(p.FirstName); } );
1 голос
/ 01 мая 2009

Вы также можете настроить XML с помощью StructureMap. Вы даже можете смешать их, если хотите.

Существуют также атрибуты StructureMap, которые вы можете поместить в свой класс Bob, чтобы сообщить StructureMap, как загрузить сборку. DefaultConstructor - это то, чем я пользуюсь время от времени.

0 голосов
/ 03 мая 2009

Что мы делаем в моем текущем проекте (который использует AutoFac, а не StructureMap, но я думаю, что это не должно иметь значения):

У нас есть интерфейсы, определяющие внешние службы, которые приложение использует в базовой сборке, скажем, App.Core (например, ваша PersonBase).

Тогда у нас есть реализации этих интерфейсов в Services.Real (как Bob.dll).

В нашем случае у нас также есть Service.Fake, которые используются для облегчения тестирования пользовательского интерфейса с зависимостями от других корпоративных сервисов и баз данных и т. Д.

Само клиентское приложение (в нашем случае приложение ASP.NET MVC) ссылается на App.Core.

Когда приложение запускается, мы используем Assembly.Load для загрузки соответствующей DLL-библиотеки реализации «Services» на основе параметра конфигурации.

Каждая из этих DLL имеет реализацию IServiceRegistry, которая возвращает список служб, которые она реализует:

public enum LifestyleType { Singleton, Transient, PerRequest}

public class ServiceInfo {
    public Type InterfaceType {get;set;}
    public Type ImplementationType {get;set;}
    // this might or might not be useful for your app, 
    // depending on the types of services, etc.
    public LifestyleType Lifestyle {get;set;} 
}

public interface IServiceRegistry {
    IEnumerable<ServiceInfo> GetServices();
}

... приложение находит это ServiceRegistry через отражение и перечисляет эти экземпляры ServiceInfo и регистрирует их в контейнере. Для нас этот регистр-все-службы живет в веб-приложении, но его можно (и во многих случаях желательно) разместить в отдельной сборке.

Таким образом, мы можем изолировать доменную логику от кода инфраструктуры и предотвратить обходные пути «просто один раз», когда приложение заканчивается в зависимости от прямой ссылки на код инфраструктуры. Мы также избегаем ссылки на контейнер в каждой реализации Служб.

Одна очень важная вещь, если вы делаете это: сделайте уверенным , что у вас есть тесты, которые подтверждают, что вы можете создавать каждый тип «верхнего уровня» (в нашем случае, ASP.NET MVC Controllers) с каждая потенциальная конфигурация контейнера IOC.

В противном случае довольно легко забыть реализовать один интерфейс и разбить огромные разделы вашего приложения.

0 голосов
/ 01 мая 2009

Параметр автоматической проверки работает только при соблюдении соглашений об именах, сборках и пространствах имен. Вы можете вручную настроить структурную карту с помощью свободного интерфейса. Пример:

ObjectFactory.Initialize(initialization => 
   initialization.ForRequestedType<PersonBase>()
    .TheDefault.Is.OfConcreteType<Bob>());
...