网站首页 > 技术文章 正文
简介
Ninject 是一个轻量级、易扩展的开源 .NET 依赖注入(DI)容器,适用于 .NET Framework、.NET Core、Xamarin 等多平台。
设计目标:简单直观、可测试、高可扩展性,支持多种绑定策略和拦截器(AOP)。
核心组件
组件 | 作用 |
IKernel | 容器核心接口,负责所有绑定和解析;通常使用 StandardKernel 实例 |
Module | 将相关绑定逻辑封装,按需加载 |
Binding | 描述接口到实现的映射规则 |
Scope | 绑定的生命周期管理(瞬时、单例、自定义作用域) |
Interceptor | 基于 Castle.DynamicProxy 实现 AOP 拦截功能 |
基础使用
// 1. 创建 Kernel(容器)
IKernel kernel = new StandardKernel();
// 2. 绑定(Binding):接口 → 实现
kernel.Bind<IUserRepository>().To<UserRepository>();
// 3. 解析
var repo = kernel.Get<IUserRepository>();
绑定方式(Binding)详解
绑定方法 | 含义 |
Bind<T>().To<U>() | 接口 T → 实现 U |
Bind<T>().ToConstant(instance) | 始终返回同一个预创建实例 |
Bind<T>().ToMethod(ctx => …) | 利用工厂方法动态构造实例 |
Bind<T>().ToProvider<MyProv>() | 自定义 IProvider<T> 负责实例化 |
Bind(typeof(IRepo<>)).To(typeof(Repo<>)) | 开放泛型绑定,让所有 IRepo<T> 解析为 Repo<T> |
不同生命周期的注册
// 单例模式(全局唯一实例)
kernel.Bind<IDatabase>().To<SqlDatabase>().InSingletonScope();
// 每次请求创建新实例(默认行为)
kernel.Bind<IService>().To<ServiceImplementation>();
// 每个线程唯一实例
kernel.Bind<IRequestHandler>().To<RequestHandler>().InThreadScope();
生命周期(Scope)管理
作用域 | Ninject 表达 | 说明 |
瞬时 | .InTransientScope() 默认(无额外配置) | 每次解析都创建新实例 |
单例 | .InSingletonScope() | 全局唯一一个实例 |
线程作用域 | .InThreadScope() | 每线程一个实例 |
请求作用域(Web) | .InRequestScope() | 在 Web 请求生命周期内重用(需 Web 扩展) |
自定义作用域 | .InScope(ctx => /* key */) | 按用户定义的 key 或上下文决定复用粒度 |
kernel.Bind<ICache>().To<MemoryCache>().InSingletonScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
注入方式
构造函数注入(Constructor Injection)
public class UserService : IUserService
{
private readonly IUserRepository _repo;
public UserService(IUserRepository repo)
{
_repo = repo;
}
}
Ninject 默认使用构造函数注入,自动解析所需的所有参数。
属性注入(Property Injection)
public class OrderController
{
[Inject]
public IOrderService OrderService { get; set; }
}
// 绑定配置需启用属性注入
kernel.Bind<OrderController>().ToSelf().WithPropertyValue(nameof(OrderController.OrderService));
kernel.Bind<OrderController>().ToSelf().WithPropertyInjection();
在需要的位置加 [Inject] 特性,Ninject 会为公有属性赋值。
方法注入(Method Injection)
public class ReportGenerator
{
[Inject]
public void Initialize(ILogger logger)
{
_logger = logger;
}
}
支持对任意公有方法注入,只要打上 [Inject]。
模块化(Module)
将绑定逻辑组织到继承 NinjectModule 的模块类里,便于拆分和复用。
public class RepositoryModule : NinjectModule
{
public override void Load()
{
Bind<IUserRepository>().To<UserRepository>().InRequestScope();
Bind<IOrderRepository>().To<OrderRepository>().InRequestScope();
}
}
// 加载模块
IKernel kernel = new StandardKernel(new RepositoryModule(), new ServiceModule());
// 获取服务
var userService = kernel.Get<IUserService>();
拦截器与 AOP
通过
Ninject.Extensions.Interception 与 Castle.DynamicProxy,可以在方法调用前后执行横切逻辑。
// 1. 安装扩展包: Ninject.Extensions.Interception
// 2. 定义拦截器
public class CallLogger : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine(#34;Calling {invocation.Request.Method.Name}");
invocation.Proceed();
Console.WriteLine(#34;Done {invocation.Request.Method.Name}");
}
}
// 3. 绑定并启用拦截
kernel.Bind<IService>().To<Service>()
.Intercept().With<CallLogger>();
高级特性
条件绑定
根据上下文动态选择实现:
// 仅当注入ConsumerA时使用
kernel.Bind<IService>().To<ServiceA>().WhenInjectedInto<ConsumerA>();
kernel.Bind<IService>().To<ServiceB>().When(request => request.Target.Type.IsDefined(typeof(SpecialAttribute)));
命名绑定
解决同一接口多实现的歧义:
kernel.Bind<IService>().To<ServiceImpl>().Named("Default");
kernel.Bind<IService>().To<BackupService>().Named("Backup");
IService service = kernel.Get<IService>("Backup"); // 按名称解析
工厂模式
动态创建实例:
public interface IServiceFactory
{
IService Create(bool usePremium);
}
public class ServiceFactory : IServiceFactory
{
private readonly IKernel _kernel;
public ServiceFactory(IKernel kernel)
{
_kernel = kernel;
}
public IService Create(bool usePremium)
{
return usePremium
? _kernel.Get<PremiumService>()
: _kernel.Get<StandardService>();
}
}
带参数的绑定
// 使用常量参数
kernel.Bind<IConfig>()
.To<AppConfig>()
.WithConstructorArgument("connectionString", "Server=localhost;Database=AppDb");
// 使用委托提供参数
kernel.Bind<IMailService>()
.To<MailService>()
.WithConstructorArgument("smtpServer", ctx => ConfigurationManager.AppSettings["SmtpServer"]);
缓存 Kernel 实例
// 应用生命周期内保持单例
public static class Container
{
public static IKernel Kernel { get; } = new StandardKernel();
}
作用域嵌套
using (var childScope = kernel.BeginBlock())
{
var service = childScope.Get<IService>(); // 子作用域内实例
} // 自动释放实现了 `IDisposable` 的对象
多实现解析
Bind<INotifier>().To<EmailNotifier>();
Bind<INotifier>().To<SmsNotifier>();
// 解析所有实现
var notifiers = kernel.GetAll<INotifier>(); // IEnumerable<INotifier>
kernel.Bind<IUserService>().To<UserService1>();
kernel.Bind<IUserService>().To<UserService2>();
public class MultiService
{
private readonly IEnumerable<IUserService> _services;
public MultiService(IEnumerable<IUserService> services)
{
_services = services;
}
}
与 ASP.NET Core / ASP.NET MVC 集成
ASP.NET MVC 5
使用 Ninject.Web.Mvc 扩展,在 Global.asax 中配置:
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
ASP.NET Core
Host.CreateDefaultBuilder(args)
.UseNinject((context, services) =>
{
services.AddControllers();
// 在此通过 kernel 进行 Ninject 绑定
})
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
// Program.cs
using Ninject;
using Ninject.Web.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
// 替换默认服务提供者工厂
builder.Host.UseServiceProviderFactory(new NinjectServiceProviderFactory());
// 配置 Ninject 模块
builder.Host.ConfigureContainer<IKernel>(kernel => {
kernel.Load<ApplicationModule>();
});
var app = builder.Build();
调试与问题排查
查看绑定关系
foreach (var binding in kernel.GetBindings(typeof(ILogger)))
{
Console.WriteLine(#34;Target: {binding.Target}");
}
猜你喜欢
- 2025-07-24 Channels: C# 实现高效的线程间通信
- 2025-07-24 一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试
- 2025-07-24 C# 获取Windows的系统信息(c# 获取本机的ip地址)
- 2025-07-24 C# 读取本地网络配置信息(c# 读取配置文件的方法)
- 2025-07-24 MODBUS协议在C#中的应用案例(c#modbus 程序)
- 2025-07-24 C#接口(c接口镜头)
- 2025-07-24 AngleSharp :在 C# 中轻松解析和操作 HTML/XML 文档
- 2025-07-24 C#中的9个“黑魔法”与“骚操作”
- 2025-07-24 C#.NET HttpClient 使用教程(c#开启http服务)
- 2025-07-24 C#.NET in、out、ref详解(c# .net5)
- 1517℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 594℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 521℃MySQL service启动脚本浅析(r12笔记第59天)
- 489℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 489℃启用MySQL查询缓存(mysql8.0查询缓存)
- 477℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 456℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 454℃MySQL server PID file could not be found!失败
- 最近发表
-
- PS所有滤镜的说明(六)(ps滤镜详解)
- 5款小白也能用的在线图片编辑器!电商效率飙升就靠它!
- Java变量(java变量有什么作用)
- Java面试常见问题:Java注解(java中的面试题)
- Java编程入门第一课:HelloWorld(java编程从入门到实践)
- Java基础教程:Java继承概述(java里继承的概述)
- java基础之——访问修饰符(private/default/protected/public)
- 如何规划一个合理的JAVA项目工程结构
- 将机器指令翻译成 JavaScript -- 终极目标
- Web 服务器基准测试:Go vs. Node.js vs. Nim vs. Bun
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)