• 重新整理 .net core 实践篇————依赖注入应用[二]


    前言

    这里介绍一下.net core的依赖注入框架,其中其代码原理在我的另一个整理<<重新整理 1400篇>>中已经写了,故而专门整理应用这一块。

    以下只是个人整理,如有问题,望请指点。

    正文

    为什么有这个依赖注入呢?

    假设人们面临这样一个问题。

    比如说一个人做飞机去北京。那么人和飞机有什么关系呢?人和北京有什么关系呢?

    假设有3个主体类,一个是人一个是飞机一个是北京。他们之间到底应该怎么组织呢?

    先说人和北京的关系,人要去北京,那么飞机就是一个交通工具。是的,只是一个交通工具,那么人和飞机就没有直接的关系,而是人通过交通工具去了北京。

    同样北京也可以抽象处理,北京是一个地点。那么这个语句就变成了"人通过交通工具去了某个地方",这个交通工具可以是飞机可以是高铁,未来可以是火箭。地点同样如此。

    如果抓住这个词,“未来”,那么其扩展性就很强了,不仅现在拥有的还代表以后。就像我们的usb接口一样,未来可能有新的设备只要适配了usb接口,那么我主机里面的东西将不用动任何东西就可以运行新的设备。

    因为我的主机没有动任何东西,那么是不是代表我的稳定性?所以依赖注入不仅可以确保我们代码的可维护性和可扩展性,最重要的是稳定性(当然了稳定性是可维护性的一种),而代码最重要的就是稳定性。

    在asp.net core 的整个架构中,依赖注入框架提供了对象创建和生命周期管理的核心能力,各个组件相互协作,可以说是整个框架的核心了。

    那么这里有一个关键词生命周期,将会伴随着整篇文章的核心。

    .net core 的依赖实现主要是通过 Microsoft.Extensions.DependencyInjection;

    其这个包,是实现了Microsoft.Extensions.DependencyInjection.Abstrations;

    Microsoft.Extensions.DependencyInjection.Abstrations是接口的定义,Microsoft.Extensions.DependencyInjection 是接口的实现。

    那么这里就可以想象到既然提供了接口,那么一般来说就有第三方实现,后面会介绍第三方实现。

    依赖注入的核心由下面几个部门组成。

    1. IServiceCollection 负责服务的注册

    2. ServiceDescriptor 每一个服务注册时候的信息

    3. IServiceProvider 具体的容器

    4. IServiceScope 容器的子容器的生命周期

    那么什么是生命周期呢?

    1. 单例模式

    2. 作用域模式

    3. 瞬时模式

    那么这个怎么理解呢?首先建立起这样一个模型。

    这个模型是什么意思呢? 首先我们创建单例模式产生的对象会存在root中,如果作用域模式那么创建的对象将会在某个作用域中,这个作用域是自己指定的。

    而瞬时模式呢,就是不存储在任何一个作用域中。

    当我们通过这个依赖注入服务创建对象的时候,如果是单例模式,那么会这么干,查找root容器中有没有该注册信息的实例,如果有就直接返回,如果没有那么创建存储在root容器中。

    如果是作用域模式,那么会去找当前作用域有没有该注册信息的实例,如果有那么直接返回,如果没有那么存储在当前作用域的容器中。

    同样,如果作用域模式,那么该作用域的容器消失了,里面存储的东西就没了,那么自然通过该作用域创建的对象就消失了。

    那么就用代码来实验一下。

    有三个类:

    里面的内容就是一个接口,然后一个具体的类,这里演示其中一个。

    public interface IMySingletonService
    {
    }
    public class MySingletonService: IMySingletonService
    {
    }
    

    然后再configureServices 中进行注册:

    services.AddSingleton<IMySingletonService, MySingletonService>();
    services.AddScoped<IMyScopedService, MyScopedService>();
    services.AddTransient<IMyTransientService, MyTransientService>();
    

    然后写一个http的get方法。

    [HttpGet]
    public int GetService([FromServices] IMySingletonService mySingleton1,
    	[FromServices] IMySingletonService mySingleton2,
    	[FromServices]IMyScopedService myScoped1,
    	[FromServices] IMyScopedService myScoped2,
    	[FromServices] IMyTransientService myTransient1,
    	[FromServices] IMyTransientService myTransient2)
    {
    	Console.WriteLine($"singleton1:{mySingleton1.GetHashCode()}");
    	Console.WriteLine($"singleton1:{mySingleton2.GetHashCode()}");
    	Console.WriteLine($"myScoped1:{myScoped1.GetHashCode()}");
    	Console.WriteLine($"myScoped2:{myScoped2.GetHashCode()}");
    	Console.WriteLine($"myTransient1:{myTransient1.GetHashCode()}");
    	Console.WriteLine($"myTransient2:{myTransient2.GetHashCode()}");
    	return 1;
    }
    
    

    这里通过hashcode 来验证是否两个对象是否相等。

    截图如下:

    然后我们再访问一次这个接口。

    上述可以得出,每一个http请求将会在同一个子容器中,且同一子容器获取的注册对象相同。而单例每次都相同。

    transient 则是每次获取到不同的对象。

    好了,现在生命周期明了了,下面看一下注册的方式。
    我们通过这种方式进行注册:

    services.AddSingleton<IMySingletonService, MySingletonService>();
    

    那么是否还有其他方式?

    还可以这样:

    services.AddSingleton<IMySingletonService>(new MySingletonService());
    services.AddSingleton<IMySingletonService>(ServiceProvider =>
    {
    	return new MySingletonService();
    });
    

    为什么需要这样呢?道理也是很简单的,因为我们可能需要一些自己定制的动态参数啊。
    比如说:

    services.AddSingleton<IMySingletonService>(ServiceProvider =>
    {
    	return new MySingletonService();
    });
    

    这种工厂模式,完全可以通过其他的注册服务(ServiceProvider)的参数来实例化我们的MySingletonService。

    还有值得注意的是,因为我们可以多次注入。那么我们按道理可以获取到注册的全部。可以通过这种方式。

    比如说:

    services.AddSingleton<IMySingletonService, MySingletonService>();
    services.AddSingleton<IMySingletonService>(new MySingletonService());
    services.AddSingleton<IMySingletonService>(ServiceProvider =>
    {
    	return new MySingletonService();
    });
    

    通过AddSingleton调用了3次。

    [HttpGet]
    public int GetService([FromServices] IEnumerable<IMySingletonService> mySingleton1)
    {
    	foreach (var item in mySingleton1)
    	{
    		Console.WriteLine(item.GetHashCode());
    	}
    
    	return 1;
    }
    

    然后获取一下。

    发现可以一对多了,分别获取到3个不同注册的。

    那么如果我们的逻辑比较复杂,可能会多次调用到怎么破?

    services.AddSingleton<IMySingletonService, MySingletonService>();
    services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>());
    services.TryAddEnumerable(ServiceDescriptor.Singleton<IMySingletonService, MySingletonService>());
    

    可以通过这种方式调用,如果注册信息相同就不注册,如果不同就注册。

    这样就只有一个。

    如果你只想注册一个的话可以这样:

    services.AddSingleton<IMySingletonService, MySingletonService>();
    services.TryAddSingleton<IMySingletonService, MySingletonService>();
    services.TryAddSingleton<IMySingletonService, MySingletonService>();
    

    这个就是如果有注册信息,就不再注册了。

    还有其他的如替换注册信息,移除注册信息等,可以参考官网。

    然后可以关注一下这个using Microsoft.Extensions.DependencyInjection.Extensions;,这里面是一些扩展的,如有自己需要的一般都在这里面了。

    另一个值得注意的地方就是泛型模板的注册。

    public interface IGeneticService<T>
    {
    	public T getData();
    }
    
    public class GeneticService<T>: IGeneticService<T>
    {
    	public T Data { get; private set; }
    
    	public GeneticService(T data)
    	{
    		this.Data = data;
    	}
    
    	public T getData()
    	{
    		return Data;
    	}
    }
    

    那么通过这种注册:

    services.AddSingleton(typeof(IGeneticService<>),typeof(GeneticService<>));
    

    然后实现一个接口:

    [HttpGet]
    public int GetService([FromServices] IMySingletonService mySingletonService, [FromServices] IGeneticService<IMySingletonService> geneticService)
    {
    	Console.WriteLine(geneticService.getData().GetHashCode());
    	Console.WriteLine(mySingletonService.GetHashCode());
    	return 1;
    }
    

    这里埋了一个点:

    services.AddSingleton<IMySingletonService, MySingletonService>();
    services.AddSingleton<IMySingletonService>(new MySingletonService());
    services.AddSingleton<IMySingletonService>(ServiceProvider =>
    {
    	return new MySingletonService();
    });
    

    那就是上面这三种注册方式的对象释放行为是不一样的,如果不了解的话,那么可能你的服务跑着跑着内存就跑高了。下一节将会解释到。

    上述只是个人整理,如有错误,望请指出,谢谢,一天一更。上述只是应用,如代码原理,请查看<<重新整理.net core 1400篇>>。

  • 相关阅读:
    进阶面向对象(下)
    进阶面向对象(上)
    使用WIFI准备工作及配置内核——韦东山
    USB设备驱动程序1
    USB总线驱动程序
    USB驱动程序涉及的概念及框架
    I2C协议简介
    倾旋之slack主题协同
    1.影子制作
    11.快速选择工具
  • 原文地址:https://www.cnblogs.com/aoximin/p/14802644.html
Copyright © 2020-2023  润新知