Ninject 在 n 层应用程序中
Ninject in an n-tier application
虽然 DI
和 IoC containers
的概念相当简单,但我似乎在努力实现。我有一个四层应用程序,其中 UI Layer
到 Service Layer
使用 IoC
并且似乎工作完美,但 Service Layer
到 Business Layer
是一个绝对的痛苦.
我专门阅读了很多文章 Ninject
和 Class Libraries
但我仍然无法正确实施。我希望你们好心的人能给我指明正确的方向...
典型分层流程:UI层>服务层>业务层>数据层
所以如果可以显示我的 UI Layer
到 Service 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;
}
}
如您所见,UI Layer
是一个 WebApi
项目,它注入了 IApiAuthorizationService 这里没有什么特别复杂的东西。
因此,一旦构建了 ApiAuthorizationService,它就会指向许多存储库,但现在我将只添加其中的一个片段。
我们现在在引用业务层的服务层中:
public class ApiAuthorizationService : IApiAuthorizationService
{
private IApiAuthTokenRepository _iApiAuthTokenRepository;
public ApiAuthorizationService(IApiAuthTokenRepository iApiAuthTokenRepository)
{
_iApiAuthTokenRepository = iApiAuthTokenRepository;
}
}
此时我已经在 Service Layer
上安装了 Ninject 并且还创建了一个 class 来创建绑定:
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IApiAuthTokenRepository>().To<ApiAuthTokenRepository>();
}
}
基本上我被困在这一点上,不知道该去哪里,我又读了很多帖子,但很多帖子使用 Console Applications
来演示,而 Class Library
没有入口点.我在想的是添加一个 Startup.cs
来初始化绑定。
谁能用一些演示代码为我指明正确的方向?
谢谢。
您可以做您正在做的事情,并创建一个用于注册服务的位置。那行得通。但是,如果您认为对您的应用程序有好处,那么这里有一种高级方法也可以使用。
总体思路是依赖注入库和您的应用程序之间的一层。我们称这一层为 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
}
现在,您可以通过 IServiceContainer.RegisterType 手动添加您想要注册的所有内容,或者,您可以使用属性做一些更自动化的事情,例如....
[ServiceRegistration(typeof(IApiAuthorizationService))]
public class ApiAuthorizationService : IApiAuthorizationService
{
private IApiAuthTokenRepository _iApiAuthTokenRepository;
public ApiAuthorizationService(IApiAuthTokenRepository iApiAuthTokenRepository)
{
_iApiAuthTokenRepository = iApiAuthTokenRepository;
}
}
IServiceContainer.RegisterTypes 的实现将扫描其中的所有类型以查找 ServiceRegistrationAttribute。对于它们中的每一个,现在您可以通过 IServiceContainer.RegisterType()
自动注册服务类型和实现类型
这可以根据需要采取或不采取。
至于这如何解决您的问题 "using in an N-tier application"...
您可以在每个程序集中提供一个接口实现,它知道如何注册它的所有需求,并且该实现可能反过来从它所依赖的另一个程序集中调用相同的接口实现。通过这种方式,您允许每个程序集转发其依赖程序集的注册。这是一项长期战略,可能会为您提供一些有用的想法。
这里有一个粗略的想法(请记住,每个程序集中都有一个)...
public class AutoRegistration: IAutoRegistration
{
public void Register(IServiceContainer container)
{
// where the type of Add<> is IAutoRegistration
container.Add<SomeDependentNamespace.AutoRegistration>();
container.Add<SomeOtherDependentNamespace.AutoRegistration>();
}
}
IServiceContainer 将简单地收集所有已添加的不同 IAutoRegistration(它从 .Add() 中的每个 TAutoRegistration 知道此时的程序集,并且可以如前所示扫描属性的类型并注册类型之一一个。
合成根
主要用于实例化和配置 DI 容器的位置应该是应用程序入口点。这意味着您永远不会在库中实例化 DI 容器。也意味着,入口点需要知道所有依赖项并相应地配置它们。
Mark Seeman 有一篇关于原理的博客 post:Composition Root.
"Good Enough"方法
现在,在大多数项目中,我都在某种程度上违反了这种方法——它有优点也有缺点。
Ninject(和 Autofac...)以模块的概念为特色。模块是一组绑定/注册,因此它是一个(部分)容器配置规范。这些可以添加到库中并在合成根中获取。
这个概念记录在案 here。
示例:
- WpfApplication.exe
- 包含一堆
NinjectModule
- 表示组合根,其中包含以下内容:
var kernel = new StandardKernel()
和 kernel.Load(AppDomain.CurrentDomain.GetAssemblies());
- ServiceLayer.dll
- 包含一堆
NinjectModule
- BusinessLayer.dll
- 包含一堆
NinjectModule
- DataLayer.dll
- 包含一堆
NinjectModule
什么时候不应该这样做?
- 当这些 *.dll 中的任何一个应该用于多个项目/软件时 - 很可能根据项目的不同,您需要不同的绑定(容器配置)。而且这个一般不能重复使用(另见here)。
- 当您计划频繁更换容器时,这会增加工作量
在这些情况下,您应该严格遵守 Mark 的组合根概念。
虽然 DI
和 IoC containers
的概念相当简单,但我似乎在努力实现。我有一个四层应用程序,其中 UI Layer
到 Service Layer
使用 IoC
并且似乎工作完美,但 Service Layer
到 Business Layer
是一个绝对的痛苦.
我专门阅读了很多文章 Ninject
和 Class Libraries
但我仍然无法正确实施。我希望你们好心的人能给我指明正确的方向...
典型分层流程:UI层>服务层>业务层>数据层
所以如果可以显示我的 UI Layer
到 Service 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;
}
}
如您所见,UI Layer
是一个 WebApi
项目,它注入了 IApiAuthorizationService 这里没有什么特别复杂的东西。
因此,一旦构建了 ApiAuthorizationService,它就会指向许多存储库,但现在我将只添加其中的一个片段。
我们现在在引用业务层的服务层中:
public class ApiAuthorizationService : IApiAuthorizationService
{
private IApiAuthTokenRepository _iApiAuthTokenRepository;
public ApiAuthorizationService(IApiAuthTokenRepository iApiAuthTokenRepository)
{
_iApiAuthTokenRepository = iApiAuthTokenRepository;
}
}
此时我已经在 Service Layer
上安装了 Ninject 并且还创建了一个 class 来创建绑定:
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IApiAuthTokenRepository>().To<ApiAuthTokenRepository>();
}
}
基本上我被困在这一点上,不知道该去哪里,我又读了很多帖子,但很多帖子使用 Console Applications
来演示,而 Class Library
没有入口点.我在想的是添加一个 Startup.cs
来初始化绑定。
谁能用一些演示代码为我指明正确的方向?
谢谢。
您可以做您正在做的事情,并创建一个用于注册服务的位置。那行得通。但是,如果您认为对您的应用程序有好处,那么这里有一种高级方法也可以使用。
总体思路是依赖注入库和您的应用程序之间的一层。我们称这一层为 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
}
现在,您可以通过 IServiceContainer.RegisterType 手动添加您想要注册的所有内容,或者,您可以使用属性做一些更自动化的事情,例如....
[ServiceRegistration(typeof(IApiAuthorizationService))]
public class ApiAuthorizationService : IApiAuthorizationService
{
private IApiAuthTokenRepository _iApiAuthTokenRepository;
public ApiAuthorizationService(IApiAuthTokenRepository iApiAuthTokenRepository)
{
_iApiAuthTokenRepository = iApiAuthTokenRepository;
}
}
IServiceContainer.RegisterTypes 的实现将扫描其中的所有类型以查找 ServiceRegistrationAttribute。对于它们中的每一个,现在您可以通过 IServiceContainer.RegisterType()
自动注册服务类型和实现类型这可以根据需要采取或不采取。
至于这如何解决您的问题 "using in an N-tier application"... 您可以在每个程序集中提供一个接口实现,它知道如何注册它的所有需求,并且该实现可能反过来从它所依赖的另一个程序集中调用相同的接口实现。通过这种方式,您允许每个程序集转发其依赖程序集的注册。这是一项长期战略,可能会为您提供一些有用的想法。
这里有一个粗略的想法(请记住,每个程序集中都有一个)...
public class AutoRegistration: IAutoRegistration
{
public void Register(IServiceContainer container)
{
// where the type of Add<> is IAutoRegistration
container.Add<SomeDependentNamespace.AutoRegistration>();
container.Add<SomeOtherDependentNamespace.AutoRegistration>();
}
}
IServiceContainer 将简单地收集所有已添加的不同 IAutoRegistration(它从 .Add() 中的每个 TAutoRegistration 知道此时的程序集,并且可以如前所示扫描属性的类型并注册类型之一一个。
合成根
主要用于实例化和配置 DI 容器的位置应该是应用程序入口点。这意味着您永远不会在库中实例化 DI 容器。也意味着,入口点需要知道所有依赖项并相应地配置它们。 Mark Seeman 有一篇关于原理的博客 post:Composition Root.
"Good Enough"方法
现在,在大多数项目中,我都在某种程度上违反了这种方法——它有优点也有缺点。 Ninject(和 Autofac...)以模块的概念为特色。模块是一组绑定/注册,因此它是一个(部分)容器配置规范。这些可以添加到库中并在合成根中获取。 这个概念记录在案 here。
示例:
- WpfApplication.exe
- 包含一堆
NinjectModule
- 表示组合根,其中包含以下内容:
var kernel = new StandardKernel()
和kernel.Load(AppDomain.CurrentDomain.GetAssemblies());
- 包含一堆
- ServiceLayer.dll
- 包含一堆
NinjectModule
- 包含一堆
- BusinessLayer.dll
- 包含一堆
NinjectModule
- 包含一堆
- DataLayer.dll
- 包含一堆
NinjectModule
- 包含一堆
什么时候不应该这样做?
- 当这些 *.dll 中的任何一个应该用于多个项目/软件时 - 很可能根据项目的不同,您需要不同的绑定(容器配置)。而且这个一般不能重复使用(另见here)。
- 当您计划频繁更换容器时,这会增加工作量
在这些情况下,您应该严格遵守 Mark 的组合根概念。