DI (Autofac) в архитектуре плагинов: один отдельный контейнер DI на плагин в порядке? - PullRequest
8 голосов
/ 15 октября 2010

Я пытаюсь ввести DI (с Autofac ) в существующее приложение Windows Forms.

Это приложение имеет базовую архитектуру плагинов, в которой каждый плагин отображает свою собственную форму.При запуске приложение сканирует зарегистрированные сборки для типов, которые реализуют IPlugin, а затем активирует их, используя Activator.CreateInstance:

public interface IPlugin
{
    Form MainForm { get; }
}

I не может изменить данную заданную структуру.Это означает, что каждый класс плагина создается не средствами DI, и поэтому мне кажется, что мне придется загружать отдельный контейнер DI для каждого плагина.

Мой вопрос ,Создание отдельного ContainerBuilder и контейнера для плагина в порядке и все еще достаточно эффективно?(Будет приблизительно 10 различных плагинов.) Или для всего приложения должен быть только один DI-контейнер?

Ниже приведен пример кода моего текущего решения.


using Autofac;
using System.Windows.Forms;

public class Plugin : IPlugin  // instantiated by Activator
{
    public Form MainForm { get; private set; }

    public Plugin()  // parameter-less constructor required by plugin framework
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule(new Configuration());
        var container = builder.Build();

        MainForm = container.Resolve<MainForm>();
        // ^ preferred to new MainForm(...) because this way, I can take
        //   advantage of having dependencies auto-wired by the container.
    }
}

internal class Configuration : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<MainForm>().SingleInstance();
        // ... more plugin-specific registrations go here...
    }
}

internal class MainForm : Form { /* ... */ }

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

Ответы [ 2 ]

7 голосов
/ 15 октября 2010

В идеале использование контейнера должно следовать шаблону Register Resolve Release (RRR).Я знаю, что вы сказали, что не можете изменить текущее использование Activator.CreateInstance, но все же может быть полезно понять, каким оно должно быть на самом деле.

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

Теперь, поскольку вы не можете этого сделать, следующая лучшая вещь, которую вы можете сделать, - это иметь контейнер для плагина, как вы предлагаете.В основном это становится деталью реализации на этом этапе.В каждом плагине вы все равно должны следовать шаблону RRR.

Это будет неэффективно?Если у вас не много плагинов и вы все время создаете и уничтожаете их, пара разных контейнеров не должна иметь большого значения.Однако лучше измерить, чем создавать преждевременные оптимизации.

В этом сценарии вы можете совместно использовать контейнер, только сделав его статичным.Тем не менее, это делает вещи более сложными, чем они должны быть, поэтому не идите по этому пути, если в этом нет крайней необходимости.

1 голос
/ 06 февраля 2012

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

Глядя на мой собственный вопрос много месяцев спустя, я осмелюсь сказать, что просто забыть о container нехорошо, поскольку (а) это IDisposable и должно рассматриваться как таковое, и (б)время жизни некоторых компонентов связано с контейнером, поэтому время жизни контейнера должно быть явно закончено;будь то в методе Dispose формы или когда вызывается его событие FormClosed.

...