【问题标题】:Where to locate Ninject modules in a multi-tier application在多层应用程序中定位 Ninject 模块的位置
【发布时间】:2012-08-16 10:41:48
【问题描述】:

我的应用程序包括许多后端程序集(包括实体框架数据存储层),这些程序集由许多前端程序集(包括 Windows 服务和 MVC3 Web 应用程序)共享。

我对 Ninject 绑定过程的理解是,每个包含可注入类型的程序集还应该包含一个 Ninject 模块,该模块定义了这些类型的默认绑定。然后将定义的模块集加载到使用程序集的 Ninject 内核中。

但是,我遇到了问题,因为所需的绑定范围并不总是一致的。例如,我的 MVC 项目需要绑定到数据上下文 InRequestScope,而 Windows 服务绑定到同一个类 InThreadScope

我显然可以通过将所有模块重新定位到前端项目中来解决这个问题,从而为每个使用场景维护每个模块的单独副本,但这似乎很老套,因为它在多个项目中复制了大部分模块内容。

关于模块在多层应用程序中的位置是否有最佳实践,我如何才能使这与我对项目之间绑定差异的需求相协调?

非常感谢您的建议,

提姆

【问题讨论】:

  • 另见stackoverflow.com/questions/1699197/…(IIRC这个Q是一个骗局,但这是我目前最好的)
  • 谢谢鲁本。你说得对,这两个问题有很多共同点。我特别喜欢您将运行时参数传递到支持程序集中的模块的建议——非常灵活。
  • 嗯;那是前一段时间的事了(没有试图以任何方式拉皮条我的答案)。过去我的字面意思可能是传递参​​数 - 一般来说,我会尽可能地通过接口来做到这一点。此外,那是在manning.com/seemann 之前,这大大减少了您会在 DI 架构中发现令人费解的问题的数量 - def run buy it no questions ask.

标签: binding module ninject


【解决方案1】:

对于具有单个应用程序的解决方案,一般建议是在应用程序项目(您的 Web 应用程序或 Web 服务项目)中注册您的容器。对于 Web 应用程序,这通常是 Global.asax Application_Start。这个将所有东西连接在一起的地方在 DI 术语中称为Composition Root

使用多应用程序解决方案,每个应用程序项目仍然有一个组合根。这是必须的,因为每个应用程序都有其独特的配置。另一方面,重复的代码总是不好的。当你引入一个新的抽象时,你不想改变三个地方。

诀窍是将所有注册向下移动到项目层次结构中。例如,您可以定义一个依赖于您的业务层程序集(及以下)的“引导程序集”,并让它拥有那些不会更改的程序集的所有注册。然后,应用程序的组合根可以使用该程序集来获取默认注册并使用特定于应用程序的依赖项对其进行扩展。

这样的事情可能看起来像这样:

// MVC Composition root
public static void Bootstrap()
{
    var container = new Container();

    // Default registrations
    BusinessLayerBootstrapper.Bootstrap(container);

    // Application specific registrations
    container.Bind<IUserContext>().To<AspNetUserContext>();

    DependencyResolver.Current = 
        new ContainerDependencyResolver(container);
}

// Windows Service Composition root
public static void Bootstrap()
{
    var container = new Container();

    // Default registrations
    BusinessLayerBootstrapper.Bootstrap(container);

    // Application specific registrations
    container.Bind<IUserContext>().To<SystemUserContext>()
        .SingleScoped();

    // Store somewhere.
    Bootstrapper.Container = container;
}

// In the BL bootstrap assembly
public static class BusinessLayerBootstrapper
{
    public static void Bootstrap(Container container)
    {
        container.Bind<IDepenency>().To<RealThing>();
        // etc
    }
}

虽然您不需要单独的引导程序程序集(您可以将此代码放置在 BL 本身中),但这可以让您的业务层程序集不受容器的任何依赖。

另外请注意,我只是调用静态 Bootstrap() 方法,而不是使用 (Ninject) 模块。我试图让我的答案独立于框架,因为您的问题是一般性的,并且所有 DI 框架的建议都是相同的。但是,如果您愿意,当然可以使用 Ninject 模块功能。

【讨论】:

  • 在这种情况下是否更建议使用“功能”或“打包”方法?
【解决方案2】:

关于作用域,MVC 应用程序需要具有与 Windows 服务不同的 CompositionRoot。我建议您尝试通过功能模块(对于那些与应用程序无关的部分)和所有其他绑定直接在 MVC 或 WindowsService 项目的 CompositionRoot 中尽可能多地进行组织。

另一个非常好的方法是定义一组通用的约定,它可以帮助您在几行代码中表达最具约束力的问题。因此,您的应用可能具有以下绑定:

MVC 应用程序

Bind(c => c.FromAssemblyContaining<IRepository>()
           .SelectAllClasses()
           .InheritedFrom<IRepository>()
           .Configure(b => b.InRequestScope()));

您的 Windows 服务应用程序

Bind(c => c.FromAssemblyContaining<IRepository>()
           .SelectAllClasses()
           .InheritedFrom<IRepository>()
           .Configure(b => b.InThreadScope()));

在我看来,结合面向特征的结构,约定方法是最干净的。

【讨论】:

    猜你喜欢
    • 2015-05-25
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 2013-12-26
    • 2015-08-28
    • 1970-01-01
    • 2011-06-22
    相关资源
    最近更新 更多