Ninject使用教程

.net framework 下可以使用 Ninject 作为 DI 容器.

关于构造函数和属性注入的对比

  • 不推荐属性注入的主要原因是:
  1. 测试困难: 属性注入导致依赖硬编码在类中,不能通过构造函数正确初始化,允许测试。
  2. 顺序依赖: 当一个类依赖于多个属性时,它们的初始化顺序可能存在依赖关系。但是属性注入无法保证依赖的正确顺序。
  3. 难以debug: 因为属性注入是在运行时发生的,调试时很难追踪并理解。
  4. 难以理解: 对于一个新代码许多人来说,属性注入的依赖关系并不易于理解。
  • 相比之下,构造函数注入则不具备这些缺点:
  1. 在构造函数中明确地声明所有的依赖
  2. 通过构造函数的参数顺序保证了正确的依赖注入顺序
  3. 在代码中明确地声明了依赖关系,易于理解和调试
  4. 允许通过传递 mock 依赖实现单元测试

Ninject 简单示例

  1. StandardKernel 为 Ninject 的 DI 容器
  2. 使用容器完成一个接口和实现类的注入.
  3. 使用容器获取了一个接口的实现类对象.
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 常用的几种绑定方式

  1. ToSelf(), 最简单, 直接绑定接口和实现类
  2. To (), 最常见, 显式绑定接口到实现类
  3. ToMethod(), 最强大, 绑定到一个动态创建的实现类实例
  4. 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 真实案例代码

代码讲解:

  1. BusinessService 有logger和name两个构造函数参数
  2. 我们分别为这两个参数绑定值或依赖
  3. 使用 ToSelf(), BusinessService 本身作为依赖
  4. 然后解析 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 中进行解析实例. 要点:

  1. 将 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>();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. .NET Core的优势与特点: - 跨平台:可以在Windows、Linux、macOS等操作系统上运行。 - 开放源代码:.NET Core是开源的,可在GitHub上查看和贡献代码。 - 高性能:.NET Core采用了新的运行时和优化技术,能够提供更快的执行速度。 - 简化开发.NET Core提供了很多常用的API和工具,使得应用程序的开发变得更加简单。 - 模块化:.NET Core可以根据需要选择和加载所需的模块,减少不必要的依赖。 2. 对于C#中的委托(Delegate)的了解: 委托是一种类型安全的函数指针,它可以将一个或多个方法封装成一个委托实例,然后将该实例传递给其他方法作为参数或存储在变量中,从而实现方法的回调。 3. MVC架构模式的组件: - Model:模型层,用于表示应用程序的数据和业务逻辑。 - View:视图层,用于展示数据和与用户交互。 - Controller:控制器层,用于接收用户请求并处理它们,然后调度适当的模型和视图来响应请求。 4. 对于.NET中的反射机制(Reflection)的了解: 反射机制是指在运行时动态地获取和操作程序元素的能力,如类型、方法、属性、字段等。它可以用来实现很多高级功能,如动态加载程序集、创建对象、调用方法、获取属性等。 5. 数据库事务(Transaction)的理解和使用注意事项: 事务是一组相关的数据库操作,要么全部执行成功,要么全部回滚。它可以保证数据的一致性和可靠性。使用事务时需要注意以下几点: - 事务应该尽量的短。 - 事务中的操作应该尽量简单,避免复杂的逻辑。 - 在事务中应该尽量使用索引,避免锁表。 - 对于长时间运行的事务,应该考虑设置超时时间。 6. 对于SignalR的使用经验: SignalR是一种实时通信库,可以使得服务器端和客户端之间的通信更加简单和高效。在项目中,可以使用SignalR实现实时聊天、通知、在线用户列表等功能。通常使用Hub来处理客户端和服务器之间的通信。 7. 对于MongoDB的使用经验: MongoDB是一种文档型数据库,可以存储和查询JSON格式的数据。在使用MongoDB时,需要先创建数据库和集合,然后可以使用C#的驱动程序来进行数据的增删改查等操作。在项目中,可以使用MongoDB来存储用户信息、日志、配置等数据。 8. 对于异步编程(Async Programming)的了解和实现方式: 异步编程是指通过异步操作来提高程序的响应性和性能。在.NET中,可以使用async和await关键字来实现异步编程,从而避免线程阻塞和提高程序的吞吐量。 9. 对于.NET中的依赖注入(Dependency Injection)的了解和使用方式: 依赖注入是一种通过构造函数、属性或方法参数来注入依赖项的技术,可以提高代码的可测试性和可维护性。在.NET中,可以使用DI容器来管理依赖项,如ASP.NET Core中的内置DI容器或第三方的Autofac、Ninject等。 10. 对于.NET中的LINQ的了解和使用方式: LINQ是一种语言集成查询(Language Integrated Query)技术,可以通过类似于SQL的语法来查询各种数据源,如集合、数组、XML、数据库等。在.NET中,可以使用LINQ来进行数据过滤、排序、分组、投影等操作,从而简化代码并提高开发效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值