.net core的依赖注入学习

依赖注入(Dependency Injection,DI),简称DI,它可以降低各模块之间的耦合

首先需要安装两个Nuget包:

Microsoft.Extensions.DependencyInjection

Microsoft.Extensions.DependencyInjection.Abstractions

安装完之后要在主程序里面引用第一个包

using Microsoft.Extensions.DependencyInjection

提前准备好一个接口和两个实现类

public interface ITestService
{
    public string Name { get; set; }
    public void SayHi();
}
public class TestServiceImpl : ITestService
{
    public string Name { get; set; }
    public void SayHi()
    {
        Console.WriteLine($"Hi, I'm {Name}");
    }
}
    public class TestServiceImpl2 : ITestService
    {
        public string Name { get; set; }
        public void SayHi()
        {
            Console.WriteLine($"你好,我是{Name}");
        }
    }

然后在主程序中调用,以下是一个服务从注册到被使用的代码

//首先声明一个容器服务的对象,将其命名为services
//然后将需要被注册服务的对象TestServiceImpl添加放入容器服务对象services中
//添加方式有三种
//services.AddTransient
//services.AddSingleton
//services.AddScoped
//添加完服务之后使用services.BuildServiceProvider()方法构造出服务容器sp
//使用sp.GetService<TestServiceImpl>()即可使用刚刚添加进去的TestServiceImpl,如果没有对象被添加进服务,则是Null;
//sp.GetRequiredService,如果没有对象被添加进服务则会抛异常
//如果注册了多个服务,使用的是GetService,那么实际上选择的是最后一个服务(此处未体现)

ServiceCollection services = new ServiceCollection();
//services.AddTransient<TestServiceImpl>();
//services.AddSingleton<TestServiceImpl>();
services.AddScoped<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
    TestServiceImpl t1 = sp.GetService<TestServiceImpl>();
    testService.Name = "tom";
    testService.SayHi();
}

关于三种添加方式AddTransient(瞬态),AddSingleton(单例),AddScoped(范围);

使用AddTransient添加到服务的对象,每一次获取服务sp.GetService都是新的

而使用AddSingleton添加到服务的对象,每一次获取服务sp.GetService都是原来的

ServiceCollection services = new ServiceCollection();
services.AddTransient<TestServiceImpl>();
services.AddSingleton<TestServiceImpl2>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
    //使用AddTransient添加的
    /*输出结果:
      Hi, I'm xiaoli
	  False
	  Hi, I'm dali
	  Hi, I'm xiaoli
	*/
    TestServiceImpl t1 = sp.GetService<TestServiceImpl>();
    t1.Name = "xiaoli";
    t1.SayHi();
    
    TestServiceImpl t2 = sp.GetService<TestServiceImpl>();
    Console.WriteLine(object.ReferenceEquals(t1,t2));
    t2.Name = "dali";
    t2.SayHi();
    t1.SayHi();
    
    //使用AddSingleton添加的
    /*输出结果:
      你好,我是xiaoli
	  True
	  你好,我是dali
	  你好,我是dali*/
    TestServiceImpl2 tt1 = sp.GetService<TestServiceImpl2>();
	tt1.Name = "xiaoli";
	tt1.SayHi();

	TestServiceImpl2 tt2 = sp.GetService<TestServiceImpl2>();
	Console.WriteLine(object.ReferenceEquals(tt1, tt2));
	tt2.Name = "dali";
	tt2.SayHi();
	tt1.SayHi();
}

如果使用的是AddScoped,并且还限定了范围,那么在同一个范围内每次获取到的对象都是一样的

ServiceCollection services = new ServiceCollection();
services.AddScoped<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
    TestServiceImpl tt1;
    //在iss中获取Scope相关对象,iss1.ServiceProvider,而不是sp
    using (IServiceScope iss1 = sp.CreateScope())
    {
        TestServiceImpl t1 = iss1.ServiceProvider.GetService<TestServiceImpl>();
        t1.Name = "xiaoli";
        t1.SayHi();

        TestServiceImpl t2 = iss1.ServiceProvider.GetService<TestServiceImpl>();
        Console.WriteLine(object.ReferenceEquals(t1, t2));
        Console.WriteLine(t1.GetType());
        tt1 = t1;
    }

    using (IServiceScope iss2 = sp.CreateScope())
    {
        TestServiceImpl t1 = iss2.ServiceProvider.GetService<TestServiceImpl>();
        t1.Name = "xiaoli";
        t1.SayHi();

        TestServiceImpl t2 = iss2.ServiceProvider.GetService<TestServiceImpl>();

        //true 同一个范围内的对象相同
        Console.WriteLine(object.ReferenceEquals(t1, t2));

        //false,两个范围(scope)的对象不相同
        Console.WriteLine(object.ReferenceEquals(tt1, t2));
    }
}

/*
Hi, I'm xiaoli
True
ioc1.TestServiceImpl
Hi, I'm xiaoli
True
False
*/

生命周期的选择:如果类无状态,建议为Singleton;如果类有状态,且有Scope控制,建议为Scoped,因为通常这种Scope控制下的代码都是运行在同一个线程中的,没有并发修改的问题;在使用Transient的时候要谨慎。

简单应用:

创建3个接口和对应的实现类,再建一个服务容器类

//日志信息
interface ILog
{
    public void Log(string msg);
}

class LogImpl : ILog
{
    public void Log(string msg)
    {
        Console.WriteLine($"日志信息:{msg}");
    }
}

//配置信息
interface IConfig
{
    public string GetValue(string name);
}

class ConfigImpl : IConfig
{
    public string GetValue(string name)
    {
        return "Hello";
    }
}

//保存信息
interface IStorage
{
    public void Save(string content, string name);
}
class StorageImpl : IStorage
{
    private readonly IConfig config;
    public StorageImpl(IConfig config)
    {
        this.config = config;
    }
    public void Save(string content, string name)
    {
        string server = config.GetValue("server");
        Console.WriteLine($"向服务器:{server}的文件名为:{name}上传{content}");
    }
}

//服务容器
class Controller
{
    private readonly ILog log;
    private readonly IStorage storage;
    public  Controller(ILog log, IStorage storage)
    {
        this.log = log;
        this.storage = storage;
    }
    public void  Test()
    {
        log.Log("开始上传");
        storage.Save("4646461jhk", "1.txt");
        log.Log("上传完毕");
    }
}

在主函数中实现

static void Main(string[] args)
{
    ServiceCollection services = new ServiceCollection();
    services.AddScoped<Controller>();
    services.AddScoped<ILog, LogImpl>();
    services.AddScoped<IStorage, StorageImpl>();
    services.AddScoped<IConfig, ConfigImpl>();

    using(ServiceProvider sp = services.BuildServiceProvider())
    {
        var c = sp.GetRequiredService<Controller>();
        c.Test();
    }
    Console.ReadKey();
}

/*输出结果
日志信息:开始上传
向服务器:Hello的文件名为:1.txt上传4646461jhk
日志信息:上传完毕
*/

学习来自杨中科大佬在某站上面的视频

posted @ 2024-04-09 14:22  来个火龙果  阅读(109)  评论(0编辑  收藏  举报