【问题标题】:Object Lifetime, Windows Forms, and Dependency Injection (Windsor)对象生命周期、Windows 窗体和依赖注入 (Windsor)
【发布时间】:2013-01-31 14:35:06
【问题描述】:

快速背景:我已经(大部分)读完了 Mark Seemann 的“.NET 中的依赖注入”,并准备成为一名 DI 忍者。我重构了我的代码库以使用构造函数注入,使用 Windsor 连接我的对象,并且成功了!

...除了不是。我的程序最初在其整个生命周期中显示了几种形式,这最初是通过根据需要将它们全部 new() 来完成的。但是,既然我是 NIT(忍者培训),我知道 new() 是邪恶的,必须将其放逐到组合根中。

问题:我的第一个端到端使用测试涉及向用户显示我的“主”窗口。可以关闭此窗口,稍后可以再次调出新版本。这在后 DI 代码库中运行良好,直到第二次显示表单。碰撞。表格已经被处理掉了。在连接我的 DI 容器时,我告诉它使用 Form 的类来实现特定的接口,并且正如预期的那样,它为该对象提供了单例生命周期。

问题:所以我很确定解决这个问题的方法是不依赖于表单实现的接口,而是依赖于该接口的工厂。这就是我对如何进行感到困惑的地方。

我的第一个想法是创建一个与表单具有相同构造函数签名的工厂,存储所有这些依赖项,然后在需要时创建一个表单。然而,这感觉就像穷人的 DI,我已经经历了在 Windsor 容器中注册所有东西的麻烦。所以这导致我......

我的第二个想法是创建一个工厂,将 DI 容器作为依赖项,然后每次只需要 .Resolve()s (这次使用短暂的生活方式)。我怀疑我会加入 .Closed 事件并在那里释放(遵循 Register-Resolve-Release 模式)。

真正的问题:我喜欢我的第二个想法,但出于某种原因,它感觉像是一个黑客。我不知道这是因为它是(并且有更好的方法)还是我的蜘蛛侠感官还没有针对 DI 进行调整。那么,归根结底,“第二个想法”是一种有效的方法还是一种代码味道?

另外,如果我将这种方法用于不提供挂钩“关闭”事件的方法,那么我应该如何设计释放机制,以便工厂提醒/鼓励我在完成时释放对象? (在某些时候我可能会忘记……)

谢谢!

【问题讨论】:

  • 你为你的屏幕采取了什么样的生活方式?你是怎么做到的?
  • 每个“屏幕”或表单相对于应用程序的生命周期都是有限的(例如,它将在应用程序启动后开始,在应用程序结束前结束)。我希望每个表单在首次显示时都能以全新的方式开始。然后我假设一旦表单关闭,我应该在 DI 容器中释放它。
  • 在进一步思考这个问题之后,我可能偶然发现了一个可以接受的答案。我不喜欢使用包含 DI 容器的 Factory 的原因是因为我只希望我的组合根依赖于 DI DLL(在本例中为 Windsor)。但对我来说,将每个工厂声明在与其创建的项目相同的项目中对我来说是有意义的,但我不想让每个项目都引用 DI DLL。但是,如果我在项目的项目中创建工厂接口,然后使用我的组合根实现该接口,那么我会得到两者的最佳结果。

标签: c# winforms dependency-injection castle-windsor object-lifetime


【解决方案1】:

在我的下层仍然不知道 DI 容器的情况下,最终对我有用的是在下层创建一个工厂接口,然后在我的组合根层实现该工厂。

即:

namespace MyApp.InterfaceLayer
{
    public interface IFormFactory
    {
        IForm GetForm();
    }
}
namespace MyApp.CompositionRoot
{
    class FormFactory : IFormFactory
    {
        Castle.Windsor.IWindsorContainer Container { get; set; }

        public FormFactory(Castle.Windsor.IWindsorContainer container)
        {
            Container = container;
        }
        public IForm GetForm()
        {
            return Container.Resolve<IForm>();
        }
    }
}

然后我当然会注册我的表单,该表单使用短暂的生活方式实现IForm,并将我的容器注册为IWindsorContainer

到目前为止,这一直很成功!

【讨论】:

  • 那么你要找什么容器具体实现呢?我想您有多个实现 IForm 的表单?
  • 您必须在 Windsor 中注册一个实现 IForm 的具体类。这是在组合根(顶层)完成的。
  • 是的,我知道 - 但您的项目中可能有多个表单必须实现 IForm?
  • 不,只有一个。每当解析“IForm”时,它将始终解析为相同的实例(或至少相同的具体类,具体取决于生命周期)。如果您需要在其他地方使用不同的“IForm”并且您提前知道需要哪一个,那么您需要创建一些“IOtherform:IForm”并注册新界面。如果您在运行时之前不知道您需要什么,那么您需要注册一个抽象工厂,然后在运行时解析抽象工厂,然后传入需要知道它应该创建哪种表单的数据(可能使用'.Resolve'在 DI 容器上)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-07
  • 2017-06-04
相关资源
最近更新 更多