将 Castle.Windsor 与 Windows 窗体应用程序结合使用

发布于 2024-10-15 05:36:57 字数 786 浏览 5 评论 0 原文

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

我的问题在于表单、服务等的创建。在 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);

正如我在至少三个问题中指出的那样,这并不明智,因为这被认为不是 IoC 的“正确”用法。

那么如何使用 Castle.Windsor 和 Windows 窗体呢?有什么方法可以设计表单激活器之类的吗?我真的很迷失,如果我不能制作一个可以解析的静态 IoC 容器,我该怎么办?

Up until this point, I have been learning IoC/DI with Castle.Windsor using ASP.NET MVC, but I have a side project that is being done in Windows Forms, and I was wondering if there is an effective way to use it for that.

My problem is in the creation of forms, services, etc. In ASP.NET MVC, there is a sort of 'Activator' that does this under the hood, but this isn't the case in Windows Forms. I have to create a new Form like var form = new fclsMain();, so a Form like ..

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

Falls kind of short. I would basically have to do ...

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

Which as I have had pointed out in at least three of my questions isn't smart, because it's supposedly not the 'correct' usage of IoC.

So how do I work with Castle.Windsor and Windows Forms? Is there some way to design a Form Activator or something? I'm really lost, if I can't make a static IoC container that I can resolve from, what can I do?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

许久 2024-10-22 05:36:57

在这里你正在做一些不太“依赖注入”的事情......

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

“新”是问题......
您必须调用

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

fcls 类型的表单,必须通过 Fluent Registration API 正确配置 o

Here you are doing something that are not very "Dependency Injection"...

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

The "new" is the problem...
You have to call

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

the form of type fcls must be correctly configured via Fluent Registration API o

半透明的墙 2024-10-22 05:36:57

为了在整个应用程序中使用相同的 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 实际上在其快速入门指南中使用了 Windows 窗体应用程序。

编辑:

我上面显示的模式是服务定位器的变体,有些人将其称为反模式。它名声不好,除其他原因外,还因为它引用了 Windsor 来充实你的代码库。理想情况下,您应该只调用一次 container.Resolve<...>() 来创建根表单。所有其他服务和表单通过构造函数注入。

实际上,您可能需要再调用几次 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...
    }
}

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

In order to use the same Castle container throughout your entire application, create a static class like:

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;
    }
}

Then register/install all your components in the Main() method. You can also hook into the application shutdown event to call Dispose() (although this isn't critical).

Castle actually uses a Windows Forms app in their quick-start guide.

Edit:

The pattern I showed above is a variant of the service locator, which some people refer to as an anti-pattern. It has a bad reputation because, among other reasons, it liters your code base with references to Windsor. Ideally, you should only have a single call to container.Resolve<...>() to create your root form. All other services & forms are injected via constructors.

Realistically, you'll probably need a few more calls to Resolve, especially if you don't want to load every single corner of the application at startup. In the web world, the best practice is to hand off the container to the web framework. In the Windows Forms world you'll need to implement your own service locator, like above. (Yes, handing the container to the ASP.NET MVC framework is still a service locator pattern).

I've edited the above code example so that the static container is injectable; no resources are tied up in a static context. If you do end up creating your own service locator, you might also want to create a test utility like this one to make testing easier.

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;
    }
}

This makes a shortcut for creating a new container that looks a lot like the production version, but with some services replaced with mocks. Some example tests might look like:

[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...
    }
}

One last note. Creating a full container for every test can get expensive. Another option is to create the full container but only using nested containers for the actual tests.

眸中客 2024-10-22 05:36:57

正如您所说,您不必“必须”新建表格。
我使用 WinForms 并且从不调用“new FormName()”。它本身始终是一种依赖。否则我必须在构造函数中填充服务定位器调用。

我可能会使用 ServiceLocator(如另一个答案中所示)仅在最顶层。
例如,我实现了一个命令模式来拦截工具栏按钮。
看起来像这样:

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();
    }
}

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

You don't "have to" new-up a form, as you've said.
I use WinForms and never call "new FormName()". It's always a dependency itself. Otherwise I'd have to stuff the constructor full of service locator calls.

I might use a ServiceLocator (as in another answer) BUT only at the very top level.
For example I have a Command pattern implemented to intercept toolbar buttons.
Looks something like this:

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

Then, in a simplified case, this is the kind of code written everywhere else:

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

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

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

Yes, I use a "service locator" but you will hardly ever see it.
That's important to me, because having service locator calls all throughout the code (eg in every class) defeats some of the point of using dependency inversion of control & needs extra work to be testable etc

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文