.NET CORE DI 依赖注入

.NET CORE DI 依赖注入DI几个概念服务(service):对象;注册服务;服务容器:负责管理注册的服务;查询服务:创建对象及关联对象;对象生命周期:Transient(瞬态);Scoped(范围);Singleton(单例)1. .NET中使用DI(1)1....

.NET CORE DI 依赖注入

DI几个概念

服务(service):对象;

注册服务;

服务容器:负责管理注册的服务;

查询服务:创建对象及关联对象;

对象生命周期:Transient(瞬态);Scoped(范围);Singleton(单例)

1. .NET中使用DI(1)

1.测试代码见备注

2.根据类型来获取和注册服务

可以分别指定服务类型(service type)和实现类型(implementation type).这两者可能相同,也可能不同.服务类型可以是类,也可以是接口,建议面向接口编程,更灵活.

3…NET控制反转组件取名为DependencyInjection,但他包含ServiceLocator的功能

2. .NET中使用DI(2)

1.Install-Package

Microsoft.Extensions.DependencyInjection

2.using Microsoft.Extensions.DependencyInjection

3.ServiceCollection用来构造容器对象

IServiceProvider.调用ServiceCollection的BuildServiceProvider()创建的ServiceProvider,可以用来获取BuildServiceProvider()之前ServiceCollection中的对象

问题:源 Microsoft Visual Studio Offline Packages 中不存在具有此 ID 的

解决:

一、工具-->Nuget程序包管理器-->程序包管理器设置

二、 Nuget Package Manager-->程序包源

三、点击右上角的 加号按钮 ,添加一个新的Nuget源选项

四、名称:随便取

       源:https://api.nuget.org/v3/index.json

点击右侧的 更新 按钮

如果有原包源勾掉禁用,没有忽视

重新生成解决方案,下载可能需要点时间等会就行

实例代码如下:

class Program
{
    static void Main(string[] args)
    {
        ServiceCollection services = new ServiceCollection();
        services.AddTransient<TestServiceImpl>();
        using (ServiceProvider sp =  services.BuildServiceProvider())
        {
            TestServiceImpl t = sp.GetService<TestServiceImpl>();
            t.Name = "lily";
            t.SayHi();
        }
        /*TestServiceImpl t = new TestServiceImpl();
            t.Name = "tom";
            t.SayHi();
            Console.Read();*/
    }


    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}");
        }
    }

}

3.生命周期

1.给类构造函数中打印,看看不同生命周期的对象创建,使用ServiceProvider.CreateScope()创建Scpoe

2.如果一个类实现了IDisposable接口,则离开作用域之后容器会自动调用对象的Dispose方法

3.不要在长生命周期的对象中引用比它短的生命周期的对象.在ASP.NET Core中,这样做默认会抛异常.

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

5…NET注册服务的重载方法很多

static void Main1(string[] args)
{
    ServiceCollection services = new ServiceCollection();
    //services.AddTransient<TestServiceImpl>();
    //services.AddSingleton<TestServiceImpl>();
    services.AddScoped<TestServiceImpl>();
    using (ServiceProvider sp =  services.BuildServiceProvider())
    {
        /*TestServiceImpl t = sp.GetService<TestServiceImpl>();
                t.Name = "lily";
                t.SayHi();

                TestServiceImpl t1 = sp.GetService<TestServiceImpl>();
                Console.WriteLine(object.ReferenceEquals(t, t1));
                t1.Name = "tom";
                t1.SayHi();

                t.SayHi();*/
        using (IServiceScope scope1 = sp.CreateScope())
        {
            //在scope中获取Scope相关的对象,scope1.ServiceProvider而不是sp
            TestServiceImpl t = scope1.ServiceProvider.GetService<TestServiceImpl>();
            t.Name = "lily";
            t.SayHi();

            TestServiceImpl t1 = scope1.ServiceProvider.GetService<TestServiceImpl>();
            Console.WriteLine(object.ReferenceEquals(t, t1));
            t1.Name = "tom";
            t1.SayHi();

            t.SayHi();
        }

        using (IServiceScope scope2 = sp.CreateScope())
        {
            //在scope中获取Scope相关的对象,scope1.ServiceProvider而不是sp
            TestServiceImpl t = scope2.ServiceProvider.GetService<TestServiceImpl>();
            t.Name = "lily";
            t.SayHi();

            TestServiceImpl t1 = scope2.ServiceProvider.GetService<TestServiceImpl>();
            Console.WriteLine(object.ReferenceEquals(t, t1));
            t1.Name = "tom";
            t1.SayHi();

            t.SayHi();
        }
    }
    /*TestServiceImpl t = new TestServiceImpl();
            t.Name = "tom";
            t.SayHi();
            Console.Read();*/
}

4.IServiceProvider的服务定位器方法:

1.T GetService() 如果获取不到对象,则返回null

  • Obeject GetService(Type serviceType)

2.T GetRequiredService() 如果获取不到对象,则抛异常

  • Object GetRequiredService(Type serviceType)

3.IEnumerable GetService() 适用可能有很多满足条件的服务

  • IEnumerable GetService(Type ServiceType)
static void Main(string[] args)
{
    ServiceCollection services = new ServiceCollection();
    services.AddScoped<ITestService, TestServiceImpl>();
    using (ServiceProvider sp = services.BuildServiceProvider())
    {
        //GetService如果找不到服务,就返回null
        //ITestService ts1 = sp.GetService<ITestService>();
        //Required:必须的,如果找不到直接抛异常
        //显式类型转换和as
        ITestService ts1 = sp.GetRequiredService<ITestService>();
        ts1.Name = "tom";
        ts1.SayHi();
        IEnumerable<ITestService> test = sp.GetServices<ITestService>();
        Console.WriteLine(ts1.GetType());
        foreach (ITestService t in test)
        {
            Console.WriteLine(t.GetType());
        }
    }
    Console.ReadLine();
}

5.真正开始依赖注入

1.依赖注入是有"传染性"的,如果一个类的对象是通过DI创建的,那么这个类的构造函数中生命的所有服务类型的参数都会被DI赋值;但是如果一个对象是程序员手动创建的,那么这个对象就和DI没有关系,它的构造函数中生命的服务类型参数就不会被自动赋值.

2…NET的DI默认是构造函数注入

3.举例:编写一个类,连接数据库做插入操作,并且记录日志(模拟的输出),把Dao,日志都放入单独的服务类

class Program
{
    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(var sp = services.BuildServiceProvider())
        {
            Controller c = sp.GetRequiredService<Controller>();
            c.Test();
        }

        Console.ReadKey();
    }
}

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("开始上传");
        this.storage.Save("xiaomi", "1.txt");
        log.Log("上传完毕");

    }
}

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}");
    }
}

6.原理复刻

需求说明

1.有配置服务,日志服务,然后再开发一个邮件发送器服务.可以通过配置服务来从文件,环境变量,数据库等地方读取配置,可以通过日志服务来将程序运行过程中的日志信息写入文件,控制台,数据库等

2.说明:案例中开发了自己的日志,配置等接口,这只是在揭示原理,.NET有现成的

实现1

1.创建三个.NET Core类库项目,ConfigServices是配置服务的项目

LogServices是日志服务的项目

MailServices是邮件发送器的项目

然后再建一个.NET Core控制台项目引用ConfigServices项目和LogServices项目,而MailServicesConsole项目引用MailServices项目.

2.编写类库项目LogServices,创建ILogProvider接口.编写实现类ConsoleLogProvider.编写一个ConsoleLogProviderExtensions定义扩展方法AddConsoleLog,namespace和IServiceCollection一致

static void Main(string[] args)
{
    ServiceCollection services = new ServiceCollection();
    services.AddScoped<IConfigService>(s=>new IniFileConfigService {FilePath = "mail.ini" });
    //services.AddScoped<IConfigService,EnvVarConfigService>();
    services.AddScoped<IMailService, MailServiceImpl>();
    //services.AddScoped<ILogProvider, ConsoleLogProvider>();

    services.AddConsoleLog();

    using (var sp = services.BuildServiceProvider())
    {
        //第一个对象只能用ServiceLocator
        IMailService mailService = sp.GetRequiredService<IMailService>();
        mailService.Send("Hello", "trump@usa.gov", "Make great again");
    }

    Console.Read();
}

本文标题为:.NET CORE DI 依赖注入

基础教程推荐