将Castle.Windsor与Windows窗体应用程序一起使用

时间:2011-01-30 02:51:08

标签: winforms inversion-of-control castle-windsor

到目前为止,我一直在使用ASP.NET MVC学习使用Castle.Windsor的IoC / DI,但我有一个在Windows Forms中完成的副项目,我想知道是否有一种有效的方法用它来做。

我的问题在于表单,服务等的创建。在ASP.NET MVC中,有一种“激活器”可以做到这一点,但在Windows窗体中并非如此。我必须创建一个像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 个答案:

答案 0 :(得分:1)

在这里你做的事情不是很“依赖注入”......

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

“新”是问题...... 你必须致电

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

必须通过Fluent Registration API o

正确配置fcls类型的形式

答案 1 :(得分:0)

要在整个应用程序中使用相同的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()(尽管这不是关键)。

Castle实际上在quick-start guide中使用了Windows窗体应用。

编辑:

我上面展示的模式是服务定位器的变体,有些人称之为反模式。它的声誉很差,因为除了其他原因之外,它还引用了Windsor的代码库。理想情况下,您只需拨打一次container.Resolve<...>()即可创建根表单。所有其他服务&amp;形式通过构造函数注入。

实际上,您可能需要再调用一次Resolve,特别是如果您不想在启动时加载应用程序的每个角落。在Web世界中,最佳实践是将容器交给Web框架。在Windows窗体世界中,您需要实现自己的服务定位器,如上所述。 (是的,将容器交给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...
    }
}

最后一点。为每个测试创建一个完整的容器可能会变得昂贵。另一种选择是创建完整容器,但仅使用嵌套容器进行实际测试。

答案 2 :(得分:0)

正如你所说,你没有“必须”新建一张表格。 我使用WinForms并且从不调用“new FormName()”。它本身就是一种依赖。否则,我必须将构造函数填满服务定位器调用。

我可能只在最顶层使用ServiceLocator(如另一个答案) BUT 。 例如,我实现了一个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();
    }
}

是的,我使用的是“服务定位器”,但你几乎看不到它。 这对我来说很重要,因为在整个代码中都有服务定位器调用(例如在每个类中)都会失去使用控制依赖性反转的一些点。需要额外的工作才能测试等