首页 文章

何处在多层应用程序中定位Ninject模块

提问于
浏览
14

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

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

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

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

是否存在关于模块应该位于多层应用程序中的最佳实践?如何将其与我对项目之间的差异绑定的需求相协调?

非常感谢你的建议,

蒂姆

2 回答

  • 13

    对于具有单个应用程序的解决方案,一般建议是在应用程序项目(您的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模块功能 .

  • 6

    关于作用域,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()));
    

    在我看来,结合面向功能的结构, Session 方法是最干净的 .

相关问题