Есть ли что-то вроде Unity для простых вещей, которые не требуют интерфейса? - PullRequest
4 голосов
/ 16 апреля 2010

Возможно, я неправильно использую Unity, но здесь идет. У меня есть пара приложений, которые загружают одни и те же сборки плагинов. Для всех сборок требуется библиотека, и я хочу, чтобы они имели доступ к этой библиотеке через Unity. Однако, чтобы использовать Unity или любую другую платформу IoC, мне пришлось бы написать интерфейс для этой библиотеки. Я, вероятно, сделаю это, но поскольку интерфейс на самом деле не нужен ни для чего, кроме поддержки Unity, я боюсь, что это означает, что я 1) упускаю суть или 2) неправильно применяю фреймворк. Если я избегаю чего-то, что предлагает мне DI, тогда мне придется сделать класс библиотеки одноэлементным, а затем передать его всем конструкторам плагинов или через открытое свойство, и я не хочу этого делать.

Тем не менее, и пока я на самом деле ничего не реализую с Unity, я не получу еще одну деталь - хотя Unity позволит мне запрашивать библиотеку через Resolve <>, мои плагины все равно должны будут иметь ссылку на Unity экземпляр, который создается в основных приложениях. Так что это тот случай, когда ваш единственный вариант - передать ссылку на Unity всем плагинам, но с этого момента это удобно, просто потому, что вы можете использовать Unity для доступа ко всем другим зависимостям?

UPDATE

Я понял, что упустил момент, но, надеюсь, кто-то сможет прояснить для меня - я не должен везде передавать ссылку на Unity! Мне нужно только создать контейнер в моем приложении, а затем зарегистрировать все типы. Затем, когда я создаю экземпляры всех своих плагинов, они просто должны волшебным образом использовать эти зарегистрированные интерфейсы, почти без лишних усилий, верно? В моем случае мои конструкторы должны быть без параметров, потому что мой загрузчик плагинов не может иметь дело с аргументами, и в этом случае мне придется использовать внедрение свойств, чтобы дать им доступ к интерфейсам, верно?

ДРУГОЕ ОБНОВЛЕНИЕ

Я пошел вперед и попробовал Unity. Я зарегистрировал экземпляр моего класса, который нужен всем плагинам. Я также знаю, что со временем у меня возникнет проблема с моим загрузчиком плагинов, поскольку они не содержат параметров (и мне может понадобиться передать его ссылку в Unity, чтобы они работали). Тем не менее, на данный момент я непосредственно создаю плагин, и я делаю это с помощью метода Resolve. Вот как выглядит этот код:

// app code
ICandySettings _candy_settings = new CandySettings();
IUnityContainer unity = new UnityContainer().RegisterInstance<ICandySettings>( _candy_settings);
CandyPlugin _plugin = unity.Resolve<Candy>(); // throws null reference exception, see below.

// plugin code
public class Candy
{
  [Dependency]
  ICandySettings CandySettings { get; set; }

  ...

  public Candy()
  {
    CandySettings.GetSetting("box"); // CandySettings is null!  why?  Didn't Unity do this for me?
  }
}

Так что моя проблема сейчас заключается в том, что я ожидал (учитывая мои ограниченные знания), что Unity собирается автоматически установить ссылку CandySettings плагина на любой экземпляр, зарегистрированный через RegisterInstance, но это не так.

РАБОЧИЙ ВАРИАНТ

Если я пропущу вещи с дымом и зеркалами и просто передам свой UnityContainer в конструктор плагина, тогда я могу вызвать Unity.Resolve (), чтобы установить значение моего свойства CandySettings, и все будет прекрасно. Я хотел бы знать, почему атрибут [Dependency] не делает то, что я думал, что будет Если я не ошибаюсь, мне не нужно передавать Unity каждому конструктору в моем загрузчике плагинов. Я должен просто использовать Unity.Resolve (), и он, вероятно, будет работать, если [Зависимость] работает. Однако теперь я понимаю, что все говорят о том, что выбор контейнера IoC заставит его всю вашу команду разработчиков.

MEF!

Пока MEF выигрывает битву за меня. Это довольно просто, и магический дым и зеркала прекрасно работают для моих нужд (в настоящее время). Но я все еще хотел бы, чтобы Unity работал. Я нахожу странным, что для MEF мне нужно только составлять части, а все остальное просто становится на свои места, тогда как я не могу заставить Unity просто вводить вещи автоматически, и мне нужно все разрешить с помощью ссылки на Unity. везде. Это не может быть правдой.

Больше MEF

Мне нравится идея, что я могу очень легко разрешать несколько объектов с помощью MEF, но как насчет случаев, когда я использую шаблон Strategy для определения поведения кода? В настоящее время это так же просто, как изменение ссылки с одной реализации поведения на другую, и это просто работает . Кто-нибудь делает это с MEF? Правильный ли способ сделать это, чтобы использовать ImportMany, а затем использовать дополнительный код, чтобы определить, какое поведение в списке должно вызываться?

Ответы [ 4 ]

2 голосов
/ 16 апреля 2010

Большинство контейнеров IoC могут отображать конкретные классы, а также интерфейсы, просто использование интерфейсов большинством считается лучшей практикой, поскольку это относится к проектированию и тестированию.

Если все, что вы хотите сделать, это разрешить предоставление нескольких распространенных типов, вы можете захотеть свернуть свои собственные, используя System.IServiceProvider. он встроен, поэтому вам не нужно будет каскадно связывать свои зависимости IoC с потребляющим кодом.

public class SomePlugin
{
 public SomePlugin(IServiceProvider serviceProvider)
 {
  _foo = serviceProvider.GetService(typeof(IFoo)) as IFoo;
 }
}

Или вы можете использовать одноэлементный локатор службы, как в Common Service Locator library

public class SomePlugin
{
 public SomePlugin()
 {
  _foo = ServiceLocator.Current.GetService(typeof(IFoo)) as IFoo;
 }
}

РЕДАКТИРОВАТЬ: Учитывая ваши обновления, я бы предложил использовать BuildUp для внедрения зависимостей свойств в ваши экземпляры плагина после того, как вы их создали. См. эту статью

PluginInstance plugin = // already loaded from whatever you're already doing
container.BuildUp<PluginInstance>(plugin);
2 голосов
/ 16 апреля 2010

Если ваши требования к внедрению зависимостей незначительны (что, судя по звукам, они есть), вы можете попробовать MEF . Это легкий и простой в использовании инструмент, который имеет преимущество в том, что находится в среде непосредственно в .NET 4, что означает отсутствие дополнительных требований к развертыванию при переходе на .NET 4.

Тем временем он поддерживается на 3.5 через сайт Codeplex.

MEF хорош в этом, поскольку он может работать с любым типом, а не только с интерфейсами.

При этом, если вы хотите, чтобы плагины использовали «библиотеку», предоставляемую вашим приложением, вам, скорее всего, всегда понадобится, чтобы эта библиотека была доступна и на нее ссылались ваши плагины (или хотя бы набор базовых классов). или интерфейсы для API).

0 голосов
/ 08 июля 2015

Причина, по которой Unity не работал должным образом в опубликованном примере, заключается в том, что в

[Dependency]
ICandySettings CandySettings { get; set; }

доступность по умолчанию является частной. Unity требует, чтобы свойство было общедоступным (чтобы оно могло вызывать установщик).

В последней версии Unity (сообщение об ошибке / поведение могло быть другим с более старой версией), используя ваш опубликованный код, я получаю исключение: «Свойство CandySettings для типа ConsoleApplication32.Candy не устанавливается».

Однако теперь я понимаю, что все говорят о том, как выбрать Контейнер IoC заставит его всю команду разработчиков.

Да и нет. Очевидно, что классы должны быть подключены для правильного внедрения зависимостей (надеюсь, в Composition Root ). Однако код приложения не (или не должен) ссылаться на контейнер (например, используя DependencyAttribute или передавая IUnityContainer). В опубликованном примере это можно сделать, зарегистрировав InjectionProperty для класса Candy:

ICandySettings _candy_settings = new CandySettings();
IUnityContainer unity = new UnityContainer().RegisterInstance<ICandySettings>( _candy_settings);
unity.RegisterType<Candy>(new InjectionProperty("CandySettings"));
CandyPlugin _plugin = unity.Resolve<Candy>(); 

Я (наряду со многими другими) также предпочитаю вводить зависимости в Конструкторе (Constructor Injection), и в этом случае Unity просто сделает правильную вещь и внедрит зависимость в конструкторе:

public class Candy
{
    public ICandySettings CandySettings { get; private set; }

    ...

    public Candy(ICandySettings candySettings)
    {
        this.CandySettings = candySettings;
        this.CandySettings.GetSetting("box"); 
    }
}

Регистрационный код будет таким же, как в исходном вопросе:

ICandySettings _candy_settings = new CandySettings();
IUnityContainer unity = new UnityContainer().RegisterInstance<ICandySettings>(_candy_settings);

var _plugin = unity.Resolve<Candy>(); // Everything is OK :)
0 голосов
/ 16 апреля 2010

Во-первых, у меня есть синглтон, который содержит ссылку на мой экземпляр контейнера Unity, поэтому мне не нужно передавать его везде.

Во-вторых, вы можете использовать unityContainer.RegisterInstance(myInstance); или unityContainer.RegisterType<MyClass, MyClass>();

...