Как избежать соединения с контейнером IoC - PullRequest
6 голосов
/ 26 апреля 2011

Я нахожусь в процессе разработки расширяемой платформы с использованием DI и IoC. Пользователи должны иметь возможность переопределять существующие функциональные возможности в рамках инфраструктуры, помещая свои собственные реализации в контейнер.

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

Мое текущее промежуточное решение - структурировать мои сборки следующим образом:

1) Определить абстрактные сборки, содержащие только интерфейсы.

2) Определить конкретные сборки, которые реализуют эти интерфейсы. Пользователи могут определять свои собственные для переопределения существующих функций.

3) Определить привязки контейнеров в отдельных сборках; т. е. один переплетный узел на каждый конкретный узел.

Это означает, что бетонные сборки не связаны с конкретным контейнером IoC, и они будут закрыты от изменений, если бы я использовал другой контейнер. Однако пользователям по-прежнему необходимо знать, какой контейнер используется моей платформой для записи сборок привязки, и им нужно будет выпускать новые сборки привязки, если я изменил контейнер IoC (т. Е. С Ninject на Spring).

Я что-то упустил?

Ответы [ 5 ]

9 голосов
/ 26 апреля 2011

Написать слабосвязанный код . Приложения должны зависеть от контейнеров. Рамки не должны.

2 голосов
/ 26 апреля 2011

Общий подход состоит в том, чтобы абстрагировать контейнер с помощью общего локатора службы

Автор MvcExtensions довольно успешно абстрагировал IoC.

1 голос
/ 26 апреля 2011

Common Service Locator - один из подходов, но он содержит только методы для разрешения, а не для регистрации.

Возможно, вы захотите взглянуть, как это реализовано в проекте agatha-rrsl .Существует более полное объяснение здесь , но вкратце:

  • определяет независимый от контейнера интерфейс для регистрации и разрешения типов
  • предоставляет реализации для различных контейнеров (или позволить пользователям отправлять реализации)

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

0 голосов
/ 26 апреля 2011

Я решил это с помощью атрибута и предоставил класс сканера, который используется для определения местоположения всех реализаций и интерфейсов, которые они предоставляют.

public class ComponentAttribute : Attribute
{}

// class that should be registered in the container.
[Component]
public class MyService : IMyService
{}

// Contains information for each class that should be 
// registered in the container.
public interface IContainerMapping
{
    public Type ImplementationType {get;}
    public IEnumerable<T> ImplementedServices {get; }
}

public class ComponentProvider
{
    public static IEnumerable<IContainerMapping> Find() 
    {
        var componentType = typeof(ComponentAttribute);
        foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
        {
           if (type.GetCustomAttributes(componentType, false).Count == 0)
              continue;

           var mapping = new ContainerMapping(type);
           List<Type> interfaces new List<Type>();
           foreach (var interfac in type.GetInterfaces())
           {
             //only get our own interfaces.
             if (interface.Assembly != Assembly.GetExecutingAssembly())
               continue;

             interfaces.Add(interfac);
           }

           mapping.ImplementedServices = interfaces;
           yield return mapping;
        }
    }
}

Это решение дает пользователю большую гибкость. Он может предоставить свое собственное решение, используя атрибут [Component] напрямую или используя ваше решение.

Что пользователь должен сделать, это что-то вроде:

foreach (var mapping in ComponentProvider.Find())
    myContainer.Register(mapping.ImplementationType).As(mapping.ImplementedServices);

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

0 голосов
/ 26 апреля 2011

Двумя простыми вариантами может быть предоставление вашему пользователю некоторого словаря, в котором он может регистрировать сопоставления типов, или в качестве альтернативы просто предоставление ему интерфейса контейнера, который предоставляет все службы, которые, по вашему мнению, вам могут понадобиться, и позволяет пользователям поставлять свои собственные упакованные контейнеры. Извиняюсь, если они не совсем соответствуют вашему сценарию, я в основном использую Unity, поэтому я не знаю, делают ли Ninject и т. Д. Причудливые вещи, которые Unity не делает.

...