Использование Castle.Windsor с приложениями Windows Forms - PullRequest
2 голосов
/ 30 января 2011

До этого момента я изучал IoC / DI с Castle.Windsor с использованием ASP.NET MVC, но у меня есть побочный проект, который выполняется в Windows Forms, и мне было интересно, существует ли эффективный способ используйте это для этого.

Моя проблема заключается в создании форм, служб и т. Д. В ASP.NET MVC есть своего рода «Активатор», который делает это под капотом, но в Windows Forms это не так. Я должен создать новую форму, такую ​​как var form = new fclsMain();, поэтому форму, такую ​​как ..

class fclsMain : System.Windows.Forms.Form
{
 private readonly ISomeRepository<SomeClass> someRepository;
 fclsMain(ISomeRepository<SomeClass> someRepository)
 {
  this.someRepository = someRepository;
 }
}

Водопад вроде короткого. Я бы в основном должен был сделать ...

var form = new fclsMain(IoC.Resolve<ISomeRepository<SomeClass>);

Что, как я уже указывал, по крайней мере, в трех из моих вопросов, не является умным, потому что якобы это не «правильное» использование IoC.

Так как мне работать с Castle.Windsor и Windows Forms? Есть ли какой-нибудь способ создать Form Activator или что-то еще? Я действительно потерян, если я не могу сделать статический IoC контейнер, из которого я могу разрешить, что я могу сделать?

Ответы [ 3 ]

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

Здесь вы делаете что-то, что не очень "Внедрение зависимости" ...

var form = new fclsMain(IoC.Resolve<ISomeRepository<SomeClass>);

"Новое" - это проблема ... Вы должны позвонить

var form = IoC.Resolve<fcls>();

форма типа fcls должна быть правильно настроена через API регистрации Fluent o

0 голосов
/ 14 августа 2013

Вы не должны обновлять форму, как вы сказали.Я использую WinForms и никогда не вызываю "new FormName ()".Это всегда сама зависимость.В противном случае мне пришлось бы заполнить конструктор полным вызовов сервисных локаторов.

Я мог бы использовать ServiceLocator (как в другом ответе) НО только на самом верхнем уровне.Например, у меня есть шаблон Command для перехвата кнопок панели инструментов.Выглядит примерно так:

public void Handle(string commandName)
{
    var command = IoC.Resolve<ICommand>(RegisteredCommands[commandName]);
    command.Execute();
}

Тогда, в упрощенном случае, этот код написан везде:

public class ShowOptionsCommand : Command, ICommand
{
    private readonly IOptionsView _optionsView;

    public ShowOptionsCommand(IOptionsView optionsView)
    {
        _optionsView = optionsView;
    }

    public void Execute()
    {
        _optionsView.Show();
    }
}

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

0 голосов
/ 30 января 2011

Чтобы использовать один и тот же контейнер Castle во всем приложении, создайте статический класс, например:

public static class CastleContainer {
    private static IWindsorContainer container;

    public static IWindsorContainer Instance {
        get {
            if (container == null) {
                container = new WindsorContainer();
            }
            return container;
        }
        // exposing a setter alleviates some common component testing problems
        set { container = value; }
    }

    // shortcut to make your life easier :)
    public static T Resolve<T>() {
        return Instance.Resolve<T>();
    }

    public static void Dispose() {
        if (container != null) 
            container.Dispose();
        container = null;
    }
}

Затем зарегистрируйте / установите все свои компоненты методом Main(). Вы также можете подключиться к событию завершения работы приложения, чтобы вызвать Dispose() (хотя это не критично).

Касл фактически использует приложение Windows Forms в своем кратком руководстве .

Edit:

Шаблон, который я показал выше, является вариантом сервисного локатора, который некоторые люди называют анти-паттерном. У него плохая репутация, потому что, помимо прочего, он освещает вашу базу кода ссылками на Виндзор. В идеале вам нужно всего лишь один вызов container.Resolve<...>(), чтобы создать корневую форму. Все остальные сервисы и формы внедряются через конструкторы.

Реально вам, вероятно, понадобится еще несколько вызовов Resolve, особенно если вы не хотите загружать каждый угол приложения при запуске. В мире Интернета лучшая практика - передать контейнер веб-инфраструктуре. В мире Windows Forms вам нужно реализовать свой собственный локатор служб, как описано выше. (Да, передача контейнера в каркас ASP.NET MVC по-прежнему является шаблоном поиска служб).

Я отредактировал приведенный выше пример кода так, что статический контейнер можно вводить; никакие ресурсы не связаны в статическом контексте. Если вы в конечном итоге создадите свой собственный локатор служб, вы также можете создать такую ​​утилиту для тестирования, чтобы упростить тестирование.

public static class TestUtilities 
{
    public static IContainer CreateContainer(Action<IContainer> extraConfig = null) 
    {
        var container = new WindsorContainer();
        // 1. Setup common mocks to override prod configuration
        // 2. Setup specific mocks, when provided
        if (extraConfig != null)
            extraConfig(container);
        // 3. Configure container with production installers
        CastleContainer.Instance = container;
        return container;
    }
}

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

[Test]
public void SubComponentWorksGreat() 
{
    using (var container = TestUtilities.CreateContainer())
    {
        var subComponent = container.Resolve<SubComponent>();
        // test it...
    }
}

[Test]
public void SubComponentWorksGreatWithMocks() 
{
    var repoMock = new Mock<IRepository>();
    using (var container = TestUtilities.CreateContainer(c => 
            c.Register(Component.For<IRepository>().Instance(repoMock.Object))))
    {
        var subComponent = container.Resolve<SubComponent>();
        // test it with all IRepository instances mocked...
    }
}

Последнее замечание. Создание полного контейнера для каждого теста может стоить дорого. Другим вариантом является создание полного контейнера, но только с использованием вложенных контейнеров для реальных испытаний.

...