Where to locate Ninject modules in a multi-tier application

泪湿孤枕 提交于 2019-11-30 02:36:35

For a solution with a single application, the general advice is to register your container in the application project (your web app, or web service project). For a web application this would typically be the Global.asax Application_Start. This place where you wire everything together is called the Composition Root in DI terminology.

With a multi-application solution, you would still have a single composition root per application project. This has to be, since every application has its unique configuration. On the other hand, duplicated code is always bad. You don't want to have to change three places when you introduce a new abstraction.

The trick is to move all registrations down the project hierarchy. For instance you can define a single 'bootstrap assembly' that depends on your business layer assemblies (and below) and let it have all the registrations for those assemblies that don't change. The composition roots of the applications can then use that assembly to get the default registrations and extend it with the application specific dependencies.

Such a thing might look like this:

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

Although you don't need to have a separate bootstrapper assembly (you can place this code in the BL itself), this allows you to keep your business layer assemblies free from any dependencies to your container.

Also note that I'm just calling a static Bootstrap() method, instead of using (Ninject) Modules. I tried to keep my answer independent of the framework, since your question is general and the advice will be the same for all DI frameworks. However, of course you can use the Ninject module feature if you want.

Regarding scoping a MVC application needs to have a different CompositionRoot than a windows service. I suggest you try to organize as much as possible by feature modules (for those parts which are application agnostic) and all the other bindings directly in the CompositionRoot of the MVC or WindowsService project.

Another very good approach is to define a common set of conventions which helps you to express the most binding concerns in a few lines. Therefore your apps could have the following to bindings:

MVC App

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

Your Windows Service App

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

In my point of view combined with a feature oriented structuring the convention approach is the cleanest one.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!