Ninject使用教程
.net framework 下可以使用 Ninject 作為 DI 容器, 適合作為依賴注入的對象應該是功能型class, 而不是數據型class, 由DI 容器自動管理不同類的依賴關系和具體類的實現子類.
關于構造函數和屬性注入的對比
- 不推薦屬性注入的主要原因是:
- 測試困難: 屬性注入導致依賴硬編碼在類中,不能通過構造函數正確初始化,允許測試。
- 順序依賴: 當一個類依賴于多個屬性時,它們的初始化順序可能存在依賴關系。但是屬性注入無法保證依賴的正確順序。
- 難以debug: 因為屬性注入是在運行時發生的,調試時很難追蹤并理解。
- 難以理解: 對于一個新代碼許多人來說,屬性注入的依賴關系并不易于理解。
- 相比之下,構造函數注入則不具備這些缺點:
- 在構造函數中明確地聲明所有的依賴
- 通過構造函數的參數順序保證了正確的依賴注入順序
- 在代碼中明確地聲明了依賴關系,易于理解和調試
- 允許通過傳遞 mock 依賴實現單元測試
Ninject 簡單示例
- StandardKernel 為 Ninject 的 DI 容器
- 使用容器完成一個接口和實現類的注入.
- 使用容器獲取了一個接口的實現類對象.
using Ninject;
public interface IHello
{
void SayHello();
}
public class HelloImpl : IHello
{
public void SayHello()
{
Console.WriteLine("Hello from Ninject!");
}
}
class Program
{
static void Main(string[] args)
{
// Create the kernel
IKernel kernel = new StandardKernel();
// Register the interface and implementation
kernel.Bind<IHello>().To<HelloImpl>();
// Resolve the dependency
IHello hello = kernel.Get<IHello>();
// Use the dependency
hello.SayHello();
}
}
Ninject 常用的幾種綁定方式
- ToSelf(), 最簡單, 直接綁定接口和實現類
- To
(), 最常見, 顯式綁定接口到實現類 - ToMethod(), 最強大, 該方式一般是使用 lambda 表達式動態創建一個被依賴對象的實例.
- ToConstructor(), 使用一個構造函數注入實現類實例
Ninject 使用一個構造函數注入實現類實例
下面的實現類構造器有一個string參數, 代碼演示如何使用 Ninject 實現構造函數 .
interface IHello
{
void SayHello();
}
class HelloImpl : IHello
{
public HelloImpl(string name)
{
Name = name;
}
public string Name { get; set; }
public void SayHello()
{
Console.WriteLine($"Hello {Name}!");
}
}
class Program
{
static void Main(string[] args)
{
IKernel kernel = new StandardKernel();
kernel.Bind<IHello>().To<HelloImpl>();
// Bind constructor argument
kernel.Bind<string>().ToConstant("John");
IHello hello = kernel.Get<IHello>();
hello.SayHello(); // Prints "Hello John!"
}
}
Ninject 使用 ToConstructor() 實現多參數構造器注入實例
kernel.Bind<IGreetingService>().ToConstructor(
ctr => {
ctr.WithConstructorArgument("name", "John");
ctr.WithConstructorArgument("age", 30);
ctr.WithConstructorArgument("isActive", true);
}
);
Ninject 使用ToMethod() 注入實現類實例的代碼
kernel.Bind<IMyService>().ToMethod(ctx => {
if(IsTestEnv()) {
return new TestService();
} else {
return new ProdService();
}
});
Ninject 按名稱綁定
定義兩個實現類:
public interface IGreetingService {...}
public class EnglishGreeting : IGreetingService {...}
public class ChineseGreeting : IGreetingService {...}
完成綁定和解析:
//綁定
kernel.Bind<IGreetingService>().To<EnglishGreeting>().Named("english");
kernel.Bind<IGreetingService>().To<ChineseGreeting>().Named("chinese");
//解析
IGreetingService english = kernel.Get<IGreetingService>("english");
IGreetingService chinese = kernel.Get<IGreetingService>("chinese");
Ninject 如何設置綁定作用域
kernel.Bind<IMyService>().To<MyService>()
.InSingletonScope();
在 Ninject 中,還有其他作用域可用:
- InTransientScope() : Transient是默認作用域,每次解析新建實例(非單例)
- InThreadScope(): 與線程綁定,每個線程解析相同實例
- InRequestScope(): 與請求綁定,每個請求解析相同實例
- InSingletonScope: 每次解析依賴時返回相同的實例。
Ninject 真實案例代碼
代碼講解:
- BusinessService 有logger和name兩個構造函數參數
- 我們分別為這兩個參數綁定值或依賴
- 使用 ToSelf(), BusinessService 本身作為依賴
- 然后解析 BusinessService 依賴時,Ninject 將先自動創建ConsoleLogger, 然后自動創建 BusinessService
interface ILogger {
void LogInfo(string message);
}
class ConsoleLogger : ILogger {
public ConsoleLogger(string name) { ... }
public void LogInfo(string message) { ... }
}
class BusinessService {
private ILogger logger;
public BusinessService(ILogger logger, string name) {
this.logger = logger;
this.Name = name;
}
public string Name { get; }
}
class Program
{
static void Main() {
IKernel kernel = new StandardKernel();
// 綁定 logger 參數, 傳入 "App"
kernel.Bind<ILogger>().To<ConsoleLogger>();
kernel.Bind<string>().ToConstant("App");
// 綁定名稱 string 參數
kernel.Bind<string>().ToConstant("Business Service");
// 綁定服務類, 沒有接口, 所以綁定到自身
kernel.Bind<BusinessService>().ToSelf();
//Ninject 將自動使用構造器 獲取 BusinessService 對象
var service = kernel.Get<BusinessService>();
service.logger.LogInfo("Service Started!");
}
}
注冊和解析不在一個文件的示例
下面代碼中, 在Program.cs 中進行綁定一個singleton 實例, 在MainForm.cs 中進行解析實例. 要點:
- 將 Ninject 容器定義為靜態屬性, 其他類可以通過 Program.Kernel 來訪問容器
// Program.cs
static class Program
{
public static IKernel Kernel { get; }
static Program()
{
Kernel = new StandardKernel();
}
}
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// 解析依賴
var service = Program.Kernel.Get<ISomeService>();
}
}

浙公網安備 33010602011771號