什么是Ninject,你什么时候使用它?

bac*_*i32 57 c# dependency-injection ninject

我一直在帮助一些项目的朋友,有一个使用Ninject的课程.我是C#的新手,我不知道那个班级在做什么,这就是我需要了解Ninject的原因.任何人都可以解释Ninject是什么以及什么时候使用它(如果可能,请举例说明)?或者,如果你可以指出一些很棒的链接.

我试过这个问题:Ninject教程/文档?但它并没有真正帮助像我这样的初学者.

Vik*_*ova 47

Ninject是.NET的依赖注入器,模式依赖注入的实际实现(控制模式的反转形式).

假设您有两个类,DbRepository并且Controller:

class Controller {
   private DbRepository _repository;

   // ... some methods that uses _repository
}

class DbRepository {
   // ... some bussiness logic here ...
}
Run Code Online (Sandbox Code Playgroud)

那么,现在你有两个问题:

  1. 你必须小心_repository使用它.你有这样的方式:

    1. 在构造函数中手动.但是如果DbRepository的构造函数发生了变化呢?您需要重写您的Controller类,因为其他部分代码已更改.如果你只有一个Controller,这并不难,但如果你有几个依赖你的课Repository你有一个真正的问题.
    2. 您可以使用服务定位器或工厂或smth.现在您依赖于服务定位器.您有一个全局服务定位器,所有代码都必须使用它.当您需要在代码的一部分中使用一个激活逻辑以及在代码的其他部分中使用其他部分时,您将如何更改服务定位器的行为?只有一种方法 - 通过构造函数传递服务定位器.但随着越来越多的课程,你需要越来越多地通过它.无论如何,这是个好主意,但这是个坏主意.

      class Controller {
         private DbRepository _repository;
      
         public Controller() {
           _repository = GlobalServiceLocator.Get<DbRepository>()
         }
      
         // ... some methods that uses _repository
      }
      
      Run Code Online (Sandbox Code Playgroud)
    3. 您可以使用依赖注入.看看代码:

      class Controller {
         private IRepository _repository;
      
         public Controller(IRepository repository) {
            _repository = repository;
         }
      }
      
      Run Code Online (Sandbox Code Playgroud)

    现在,当你需要你的控制器时,你写:ninjectDevKernel.Get<Controller>();ninjectTestKernel.Get<Controller>();.您可以根据需要快速切换依赖项解析器之间的beet.看到?这很简单,你不需要写很多东西.

  2. 你不能对它进行单元测试.你Controller有依赖性DbRepository,如果你想测试一些使用存储库的方法,你的代码将转到数据库并询问数据.那很慢,很慢.如果您的代码DbRepository发生变化,您的单元测试Controller将会失败.在这种情况下,只有集成测试必须说"问题".您在单元测试中需要什么 - 隔离您的类并在一个测试中仅测试一个类(理想情况下 - 只有一个方法).如果你的DbRepository代码失败了,你会认为Controller代码失败了 - 这很糟糕(即使你有测试DbRepositoryController- 他们都会失败,你可以从错误的地方开始).确定错误的位置需要花费大量时间.你需要知道某些类是正常的,而其他一些类是失败的.

  3. 当你想要DbRepository在所有课程中替换其他东西时,你必须做很多工作.

  4. 你不能轻易控制一生DbRepository.此类的对象创建Controller删除时的inializing 和Controller删除.不同Controller类的实例之间没有共享,并且在其他类之间没有共享.使用Ninject,您可以简单地写:

    kernel.Bind<IRepository>().To<DbRepository>().InSingletonScope();
    
    Run Code Online (Sandbox Code Playgroud)

和依赖注入的特殊功能 - 敏捷开发!您描述了您的控制器使用带接口的存储库IRepository.你不需要写作DbRepository,你可以写简单的MemoryRepository课程,并Controller在其他人发展的同时发展DbRepository.当DbRepository将完成,你只要重新绑定在你的依赖解析器即默认IRepositoryDbRepository现在.你有很多控制器吗?他们现在都会使用DbRepository.这很酷.

阅读更多:

  1. 控制反转(维基)
  2. 依赖注入(wiki)
  3. 控制容器的反转和依赖注入模式(Martin Fowler)

  • 我仍然不明白ninject如何让它比仅手动注入更容易.如果我有每个公司的上传文件策略,并且需要在运行时根据所选公司加载策略.似乎ninject不会改变很多代码,无论我是手动还是使用ninject.我从ninject看到的唯一好处是,如果我想在测试期间写入数据库或登录到文本文件,那么我可以在启动时轻松更改策略. (3认同)

Skl*_*vvz 41

Ninject是一个控制反转容器.

它有什么作用?

假设你有一个Car依赖于Driver类的类.

public class Car 
{
   public Car(IDriver driver)
   {
      ///
   }
}
Run Code Online (Sandbox Code Playgroud)

为了使用这个Car类,你可以像这样构建它:

IDriver driver = new Driver();
var car = new Car(driver);
Run Code Online (Sandbox Code Playgroud)

IoC包含集中了有关如何构建类的知识.它是一个知道一些事情的中央存储库.例如,它知道您需要用于构建汽车的具体类是一个Driver而不是任何其他类IDriver.

例如,如果您正在开发MVC应用程序,则可以告诉Ninject如何构建控制器.您可以通过注册哪些具体类满足特定接口来实现.在运行时,Ninject将确定构​​建所需控制器所需的类,以及所有后台.

// Syntax for binding
Bind<IDriver>().To<Driver>();
Run Code Online (Sandbox Code Playgroud)

这是有益的,因为它可以让您构建更易于单元测试的系统.假设Driver封装了所有数据库访问权限Car.在Car的单元测试中,您可以这样做:

IDriver driver = new TestDriver(); // a fake driver that does not go to the db
var car = new Car(driver);
Run Code Online (Sandbox Code Playgroud)

有整个框架负责为您自动创建测试类,它们被称为模拟框架.

欲获得更多信息:


Jer*_*yal 6

其他答案很好,但我还想指出使用Ninject文章的这个实现依赖注入.
这是我读过的最好的文章之一,它解释了Dependency Injection和Ninject的一个非常优雅的例子.

这是文章的片段:

接口下面将由我们的(SMSService)和(MockSMSService)实现,基本上新的接口(ISMSService)将公开两种服务的相同行为,如下面的代码:

public interface ISMSService
 {
 void SendSMS(string phoneNumber, string body);
 }
Run Code Online (Sandbox Code Playgroud)

(SMSService)实现实现(ISMSService)接口:

public class SMSService : ISMSService
 {
 public void SendSMS(string mobileNumber, string body)
 {
 SendSMSUsingGateway(mobileNumber, body);
 }




private void SendSMSUsingGateway(string mobileNumber, string body)
 {
 /*implementation for sending SMS using gateway*/
Console.WriteLine("Sending SMS using gateway to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }
Run Code Online (Sandbox Code Playgroud)

(MockSMSService)使用相同的接口完全不同的实现:

public class MockSMSService :ISMSService
 {
 public void SendSMS(string phoneNumber, string body)
 {
 SaveSMSToFile(phoneNumber,body);
 }

private void SaveSMSToFile(string mobileNumber, string body)
 {
 /*implementation for saving SMS to a file*/
Console.WriteLine("Mocking SMS using file to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }
Run Code Online (Sandbox Code Playgroud)

我们需要对我们的(UIHandler)类构造函数实现更改以通过它传递依赖项,通过这样做,使用(UIHandler)的代码可以确定要使用的(ISMSService)的具体实现:

public class UIHandler
 {
 private readonly ISMSService _SMSService;

public UIHandler(ISMSService SMSService)
 {
 _SMSService = SMSService;
 }
 public void SendConfirmationMsg(string mobileNumber) {

 _SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
 }
 }
Run Code Online (Sandbox Code Playgroud)

现在,我们必须创建一个继承自(NinjectModule)的独立类(NinjectBindings).此类将负责在运行时解析依赖项,然后我们将覆盖用于在其中配置绑定的load事件.关于Ninject的好处是我们不需要在(ISMSService),(SMSService)和(MockSMSService)中更改我们的代码.

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
 Bind<ISMSService>().To<MockSMSService>();
 }
 }
Run Code Online (Sandbox Code Playgroud)

现在在UI表单代码中,我们将使用Ninject的绑定来确定要使用的实现:

class Program
 {
 static void Main(string[] args)
 {
 IKernel _Kernal = new StandardKernel();
 _Kernal.Load(Assembly.GetExecutingAssembly());
 ISMSService _SMSService = _Kernal.Get<ISMSService>();

UIHandler _UIHandler = new UIHandler(_SMSService);
 _UIHandler.SendConfirmationMsg("96279544480");

Console.ReadLine();
 }
 }
Run Code Online (Sandbox Code Playgroud)

现在代码使用Ninject Kernal来解析所有依赖链,如果我们想要在Release模式下使用真实服务(SMSService)(在生产环境中)而不是模拟,我们需要更改Ninject绑定类( NinjectBindings)只使用正确的实现或使用#if DEBUG指令,如下所示:

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
#if DEBUG
 Bind<ISMSService>().To<MockSMSService>();
#else
 Bind<ISMSService>().To<SMSService>();
#endif

}
 }
Run Code Online (Sandbox Code Playgroud)

现在我们的绑定类(NinjectBindings)生活在我们所有执行代码的顶部,我们可以在一个位置轻松控制配置.


另外,请参阅什么是控制反转?提到一些非常简单的例子来理解IoC.