【问题标题】:Slightly complicated object map稍微复杂的对象图
【发布时间】:2014-04-08 14:44:10
【问题描述】:

我有一个演示者来处理我的表单的行为,如下所示:

public class Form1Presenter
{
    IForm1View _view;
    IUnitOfWork _unitOfWork;
    IService1 _service1;
    IService2 _service;

    public Form1Presenter(
        IForm1View view,
        IUnitOfWork unitOfWork,
        IService1 service1,
        IService2 service2)
    {
        _view = view;
        _unitOfWork = unitOfWork;
        _service1 = service1;
        _service2 = service2;
    }
}

服务被注入了与上面的演示者相同的工作对象单元。像这样:

public class Service1 : IService1
{
    IUnitOfWork _unitOfWork;

    public Service1(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
}

要手动创建 Form1Presenter,我必须这样做:

public class Form1 : Form, IForm1View
{
    public Form1()
    {
        IUnitOfWork unitOfWork = new UnitOfWork();
        IService1 service1 = new Service1(unitOfWork);
        IService1 service2 = new Service2(unitOfWork);
        Form1Presenter presenter = new Form1Presenter(this, unitOfWork, service1, service2);
    }
}

Ninject 有什么好的方法吗?我是新手,似乎无法解决。

【问题讨论】:

  • 您必须使用作用域来确保service1service2Form1Presenter 被注入相同的IUnitOfWork 实例。另外,您的IUnitOfWork 的生命周期(管理)是多少?
  • 是的,示波器听起来像我需要的东西。为每个表单创建一个新的 IUnitOfWork 实例。
  • This is an interesting article。如果我正在编写网页代码,我会使用 InRequestScope。激活块似乎可以解决问题,但它会污染我的代码,因为我希望将这些细节限制在组合根中。
  • 不要(永远)使用ActivationBlock,它将从 ninject 中删除,并且还有一些其他副作用很多
  • 感谢您对 ActivationBlock 的提醒。就像我说的,我是 Ninject 的新手。

标签: c# winforms ninject inversion-of-control mvp


【解决方案1】:

所以您想为每个表单创建一个工作单元。我不会讨论这是否是一个明智的选择。

有几种方法可以实现这一点:

1 - 手动管理

var unitOfWork = IResolutionRoot.Get<IUnitOfWork>();
IResolutionRoot.Get<Form1Presenter>(new ConstructorArgument("unitOfWork", unitOfWork, true);

这里你有 ninject 创建一个工作单元,然后请求一个表单并将 unitOfWork 作为 ConstructorArgument 放在请求上。使用 unitOfWork 的表单及其所有依赖项需要有一个构造函数参数IUnitOfWork unitOfWork。该名称需要与构造函数参数中指定的名称相同。

所以这是一项相当多的工作,并没有利用 IoC 可以提供的所有好处。所以我不建议使用这种方法。

2 命名范围

看看Ninject Named Scope Extension。 如下创建绑定:

const string UnitOfWorkScopeName = "UnitOfWorkScopeName";

IBindingRoot.Bind<IUnitOfWork>().To<UnitOfWork>()
            .InNamedScope(UnitOfWorkScopeName);

IBindingRoot.Bind<Form1Presenter>().ToSelf()
            .DefinesNamedScope(UnitOfWorkScopeName);
IBindingRoot.Bind<Form2Presenter>().ToSelf()
            .DefinesNamedScope(UnitOfWorkScopeName);
IBindingRoot.Bind<Form3Presenter>().ToSelf()
            .DefinesNamedScope(UnitOfWorkScopeName);

这将导致与DefinesNamedScope 绑定的所有对象及其所有依赖项都获得IUnitOfWork 的相同实例。 但是,当您不在正确的范围内时,您永远无法实例化 IUnitOfWork。因此,您所有的表单都需要定义一个(相同的)范围。此外,一个表单可能不依赖于另一个表单,因为那样您就会有范围重叠。

3 调用范围

InCallScope() 表示由一个 IResolutionRoot.Get() 创建的所有对象都将收到相同的对象。

IBindingRoot.Bind<IUnitOfWork>().To<UnitOfWork>()
            .InCallScope();

其他所有组件的绑定不需要调整。

如果您通过一次调用 IResolutionRoot.Get() 来实例化多个表单,这将不起作用


当然,还有更多方法可以管理工作单元的生命周期。 我们已将会话管理与 IUnitOfWork 分离,并使用ThreadLocal 来确定我们是否需要启动新的会话/事务或使用现有的。

【讨论】:

  • InCallScope() 听起来很有希望,我会在今天晚些时候试一试,如果一切按计划进行,我会将您的答案标记为已接受。非常感谢。
【解决方案2】:

安装Ninject.Extensions.Conventions,然后你可以像这样将所有类绑定到它们的默认接口(类名前面带有“I”)

IKernel kernel = new StandardKernel();
            kernel.Bind(x =>
                x.FromThisAssembly()         // Scans currently assembly
                 .IncludingNonePublicTypes() // Including Non-public types
                 .SelectAllClasses()         // Retrieve all non-abstract classes
                 .BindDefaultInterface());   // Binds the default interface to them, e.g. class name without preceding "I"
var Form1Presenter = kernel.Get<Form1Presenter>();

【讨论】:

    猜你喜欢
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多