简体   繁体   English

Ninject应用程序中的Ninject

[英]Ninject in an n-tier application

Whilst the concepts of DI and IoC containers are fairly straight forward I seem to be struggling with the implementation. 虽然DIIoC containers的概念相当直接,但我似乎在努力实现。 I have a four-tier application in which the UI Layer to Service Layer uses IoC and seems to be working perfect but the Service Layer to Business Layer is an absolute pain. 我有一个四层应用程序,其中UI LayerService Layer使用IoC并且似乎工作正常,但Service LayerBusiness Layer是一个绝对的痛苦。

I've read many articles specifically Ninject and Class Libraries but I'm still having trouble implementing correctly. 我已经阅读了许多专门针对NinjectClass Libraries文章,但我仍然无法正确实现。 I'm hoping you kind folk can point me in the right direction... 我希望你善良的人能指出我正确的方向......

Typical hierarchical flow: UI Layer > Service Layer > Business Layer > Data Layer 典型的分层流程: UI层>服务层>业务层>数据层

So If may show the implementation of my UI Layer to Service Layer : 那么如果可以显示我的UI LayerService Layer

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IApiAuthorizationService>().To<ApiAuthorizationService>();
    } 

    public class DebateController : ApiController
    {
        private IApiAuthorizationService _iApiAuthorizationService;
        public DebateController(IApiAuthorizationService iApiAuthorizationService)
        {
            _iApiAuthorizationService = iApiAuthorizationService;
        }
     }

As you can see the UI Layer is a WebApi project that injects the IApiAuthorizationService nothing terribly complicated here. 正如您所看到的, UI Layer是一个WebApi项目,它注入了IApiAuthorizationService,这里没什么特别复杂的。

So once ApiAuthorizationService is constructed it points to many repositories but for now I'll add a snippet of just the one. 因此,一旦构建了ApiAuthorizationService ,它就指向了许多存储库,但是现在我将添加一个只有一个存储库的片段。

We're in the Service Layer now which references the Business Layer: 我们现在位于服务层,它引用了业务层:

    public class ApiAuthorizationService : IApiAuthorizationService
    {
        private IApiAuthTokenRepository _iApiAuthTokenRepository;
        public ApiAuthorizationService(IApiAuthTokenRepository iApiAuthTokenRepository)
        {
            _iApiAuthTokenRepository = iApiAuthTokenRepository;
        }
     }

At this point I've installed Ninject on the Service Layer and also created a class that will create the bindings: 此时我已经在Service Layer上安装了Ninject ,并且还创建了一个将创建绑定的类:

public class Bindings : NinjectModule
{
    public override void Load()
    {
        Bind<IApiAuthTokenRepository>().To<ApiAuthTokenRepository>();
    }
}

Basically I'm stuck at this point and not sure where to go, again I've read many posts but many use Console Applications to demonstrate and a Class Library does not have an entry point. 基本上我已经陷入困境,不知道该去哪里,我再次阅读了很多帖子,但许多人使用Console Applications进行演示,而且Class Library没有入口点。 What I was thinking was to add a Startup.cs to initialize the bindings. 我想的是添加一个Startup.cs来初始化绑定。

Could anyone point me in the right direction with some demo code? 有人能用一些演示代码指出我正确的方向吗?

Thank you. 谢谢。

Composition Root 组成根

Primarily the location for instanciating a DI container and configuring it should be the application entry point. 主要用于实例化DI容器并配置它的位置应该是应用程序入口点。 Meaning that you dont, ever, instanciate a DI container in a library. 这意味着你永远不会在库中实现DI容器。 Also means, that the entry point needs to know all dependencies and configures them accordingly. 也就是说,入口点需要知道所有依赖关系并相应地配置它们。 Mark Seeman has a blog post about the principle: Composition Root . Mark Seeman有一篇关于原则的博客文章: Composition Root

"Good Enough" Approach “足够好”的方法

Now, in most project's i've violated this approach by some degree - which has advantages and disadvantages. 现在,在大多数项目中,我在某种程度上违反了这种方法 - 这有利有弊。 Ninject (and Autofac...) feature the concept of modules. Ninject(和Autofac ...)具有模块的概念。 A module is a set of bindings / registration, so it's a (partial) container configuration specification. 模块是一组绑定/注册,因此它是(部分)容器配置规范。 These can be added to the libraries and picked up in the composition root. 这些可以添加到库中并在组合根中获取。 The concept is documented here . 这里记录这个概念。

Example: 例:

  • WpfApplication.exe WpfApplication.exe
    • contains a bunch of NinjectModule s 包含一堆NinjectModule
    • represents composition root with something along the lines of: var kernel = new StandardKernel() and kernel.Load(AppDomain.CurrentDomain.GetAssemblies()); 表示组合根,其中包含以下内容: var kernel = new StandardKernel()kernel.Load(AppDomain.CurrentDomain.GetAssemblies());
  • ServiceLayer.dll ServiceLayer.dll
    • contains a bunch of NinjectModule s 包含一堆NinjectModule
  • BusinessLayer.dll BusinessLayer.dll
    • contains a bunch of NinjectModule s 包含一堆NinjectModule
  • DataLayer.dll DataLayer.dll
    • contains a bunch of NinjectModule s 包含一堆NinjectModule

When should you not do so? 你什么时候不这样做?

  • When any of these *.dlls are supposed to be used in multiple projects / softwares - it's likely that depending on the project you'll need different bindings (container configuration). 当任何这些* .dll应该用于多个项目/软件时 - 根据项目的不同,您可能需要不同的绑定(容器配置)。 And this can generally not be reused (also see here ). 这通常不能重复使用(也见这里 )。
  • When you plan to change containers frequently this will impose more work 当您计划经常更换容器时,这会产生更多的工作

In these cases you should strictly adhere to Mark's Composition Root concept. 在这些情况下,您应该严格遵守Mark的Composition Root概念。

You could do what you're doing, and create a single location where services are registered. 您可以执行您正在执行的操作,并创建一个注册服务的位置。 That would work. 那会有用。 But here's a high level approach that also works if you think there is a benefit to your application. 但是,如果您认为应用程序有益处,这里的高级方法也可以使用。

The general idea is a layer between the dependency injection library and your application. 总体思路是依赖注入库和应用程序之间的层。 Let's call this layer IServiceContainer. 我们将这个层称为IServiceContainer。

public class ServiceContainer : IServiceContainer
{
  // internally manages ninjects kernel via methods such as...
  public void RegisterType<TService, TInstance>();
  public void RegisterTypes(Assembly assembly);

  // ultimately, this just segregates Ninject from your app so there are no Ninject dependencies
}

Now, you could manually add all the things you want to register via IServiceContainer.RegisterType, or, you do something a bit more automatic with attributes, like.... 现在,您可以通过IServiceContainer.RegisterType手动添加要注册的所有内容,或者,您可以使用属性更自动地执行某些操作,例如....

[ServiceRegistration(typeof(IApiAuthorizationService))]
public class ApiAuthorizationService : IApiAuthorizationService
{
    private IApiAuthTokenRepository _iApiAuthTokenRepository;
    public ApiAuthorizationService(IApiAuthTokenRepository iApiAuthTokenRepository)
    {
        _iApiAuthTokenRepository = iApiAuthTokenRepository;
    }
}

The implementation of IServiceContainer.RegisterTypes would scan all the types in it for the ServiceRegistrationAttribute. IServiceContainer.RegisterTypes的实现将扫描ServiceRegistrationAttribute中的所有类型。 For each of them, now you can register the service type and implementation type automatically via IServiceContainer.RegisterType() 对于它们中的每一个,现在您可以通过IServiceContainer.RegisterType()自动注册服务类型和实现类型

This can be taken as far or not as needed. 这可以根据需要采用或不采用。

As for how this addresses your question of "using in an N-tier application"... You could provide a single interface implementation in each assembly that knows how to register all of it's needs and that implementation might in turn call the same interface implementation from another assembly it depends on. 至于如何解决你在“在N层应用程序中使用”的问题......你可以在每个程序集中提供一个单一的接口实现,它知道如何注册它的所有需求,并且实现可能反过来调用相同的接口实现从它依赖的另一个装配。 In this way, you allow each assembly to forward along the registration of it's dependent assemblies. 通过这种方式,您可以允许每个组件沿其依赖组件的注册向前移动。 It's a long term strategy that might present you with a couple useful ideas. 这是一个长期战略,可能会为您提供一些有用的想法。

Here's a rough idea of what that could look like (remember, you would have one of these in each assembly)... 这是一个粗略的概念,看起来像什么(记住,你会在每个装配中有一个)...

public class AutoRegistration: IAutoRegistration
{
    public void Register(IServiceContainer container)
    {
        // where the type of Add<> is IAutoRegistration
        container.Add<SomeDependentNamespace.AutoRegistration>();
        container.Add<SomeOtherDependentNamespace.AutoRegistration>();
    }
}

The IServiceContainer would simply collect up all the distinct IAutoRegistration's that were added (it knows the assembly at that point from each TAutoRegistration in .Add() and can scan the types for the attribute as shown earlier and register the types one by one. IServiceContainer将简单地收集添加的所有不同的IAutoRegistration(它在.Add()中知道每个TAutoRegistration的那个组件,并且可以扫描属性的类型,如前所示,并逐个注册类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM