欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

循序渐进学.Net Core Web Api开发系列【11】:依赖注入

程序员文章站 2022-05-04 23:25:49
本篇介绍如何采用依赖注入的方式创建和使用对象,主要从应用层面进行描述,不涉及具体的内部原理。 ......

系列目录

 本系列涉及到的源码下载地址:

 

一、概述

本篇介绍如何采用依赖注入的方式创建和使用对象,主要从应用层面进行描述,不涉及具体的内部原理。

 

二、演练

假设要做一个日志服务的类,它实现在控制台打印出带时间信息的日志信息。

首先定义该服务的接口与实现类。

   public interface ILogService
    {
        void LogInfomation(string info);
    }

    public class MyLogService : ILogService
    {
        void ILogService.LogInfomation(string info)
        {
            Console.WriteLine($" ==> MyLogService : {DateTime.Now.ToString()}:{info}");
        }
    }

注册该服务

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddCors();

           services.AddSingleton<ILogService, MyLogService>();
        }

注册成功,我们在Controller中使用该服务:

public class ArticleController : Controller
    {       
    private readonly ILogService _myLog; public ArticleController(ILogService myLog) { _myLog = myLog; } [HttpGet("logger")] public void TestLogger(string logger) { _myLog.LogInfomation("hahaha"); return; } }

 简单分析一下:

1、首先通过services.AddSingleton方法向依赖注入容器登记注册MyLogService;

2、在构建Controller时,根据其构造函数类型遍历其输入参数,在依赖注入容器中找到该对象并作为实参传递给构造方法。

 

三、生命周期问题

注册一个服务,根据生命周期需要的不同,有下面三种方式:

services.AddSingleton<ILogService, MyLogService>();

services.AddScoped<ILogService, MyLogService>();

ervices.AddTransient<ILogService, MyLogService>();

三种注册方式分别对应三种生命周期

1)Singleton:单例服务,从当前服务容器中获取这个类型的实例永远是同一个实例;

2)Scoped:每个作用域生成周期内创建一个实例;

3)Transient:每一次请求服务都创建一个新实例;

 

对我们的日志进行改造,让其在构建时生成一个ID,通过观察其guid变化可以理解这三种生命周期的区别。

public class MyLogService : ILogService
    {
        public  Guid _guid;

        public MyLogService()
        {
            _guid = Guid.NewGuid();
        }

        void ILogService.LogInfomation(string info)
        {
            Console.WriteLine($" ==> MyLogService : My Guid is :{_guid}");
            Console.WriteLine($" ==> MyLogService : {DateTime.Now.ToString()}:{info}");
        }
    }

 

四、通过扩展方法注册服务

通过对IServiceCollection增加扩展方法来注册服务

public static class MyLogServiceCollectionExtensions
    {
        public static void AddMyLog(this IServiceCollection services)
        {
            services.AddSingleton<ILogService, MyLogService>();
        }
    }

这样,使用者的注册代码可以修改为:

public void ConfigureServices(IServiceCollection services)
{   
  services.AddMvc();
  services.AddCors();
  services.AddMyLog(); 
}

可见AddMvc、AddCors等也是向容器注入服务。

循序渐进学.Net Core Web Api开发系列【11】:依赖注入
        public static IMvcBuilder AddMvc(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException("services");
            }
            IMvcCoreBuilder mvcCoreBuilder = MvcCoreServiceCollectionExtensions.AddMvcCore(services);
            MvcApiExplorerMvcCoreBuilderExtensions.AddApiExplorer(mvcCoreBuilder);
            MvcCoreMvcCoreBuilderExtensions.AddAuthorization(mvcCoreBuilder);
            MvcServiceCollectionExtensions.AddDefaultFrameworkParts(mvcCoreBuilder.PartManager);
            MvcCoreMvcCoreBuilderExtensions.AddFormatterMappings(mvcCoreBuilder);
            MvcViewFeaturesMvcCoreBuilderExtensions.AddViews(mvcCoreBuilder);
            MvcRazorMvcCoreBuilderExtensions.AddRazorViewEngine(mvcCoreBuilder);
            MvcRazorPagesMvcCoreBuilderExtensions.AddRazorPages(mvcCoreBuilder);
            TagHelperServicesExtensions.AddCacheTagHelper(mvcCoreBuilder);
            MvcDataAnnotationsMvcCoreBuilderExtensions.AddDataAnnotations(mvcCoreBuilder);
            MvcJsonMvcCoreBuilderExtensions.AddJsonFormatters(mvcCoreBuilder);
            MvcCorsMvcCoreBuilderExtensions.AddCors(mvcCoreBuilder);
            return new MvcBuilder(mvcCoreBuilder.Services, mvcCoreBuilder.PartManager);
        }
View Code

 

五、几个问题

1、如果多次注册会怎样

可以多次注册同一种生命周期的类,如下是可以的:

services.AddSingleton<ILogService, MyLogService>();
services.AddSingleton<ILogService, MyLogService>();
services.AddSingleton<ILogService, MyLogService>();

但下面这个代码不行:

services.AddSingleton<ILogService, MyLogService>();
services.AddScoped<ILogService, MyLogService>();

 

2、如何获取已经注册的服务列表

通过ServicesProvider可以获取服务列表

            services.AddMyLog();
            services.AddMyLog();
            services.AddMyLog();

            var provider = services.BuildServiceProvider();
            var servicesList = provider.GetServices< ILogService >();
            foreach (var service in servicesList)
            {
                Console.WriteLine("service:" + service.ToString());
            }

以上代码输出3条记录。

但下面的代码只输出一条记录:

            services.AddCors();
            services.AddCors();
            services.AddCors();          

            var provider = services.BuildServiceProvider();
            var servicesList = provider.GetServices<ICorsService>();
            foreach (var service in servicesList)
            {
                Console.WriteLine("service:" + service.ToString());
            }

具体原因看一下源码就清楚了:

public static IServiceCollection AddCors(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException("services");
    }
    services.TryAdd(ServiceDescriptor.Transient<ICorsService, CorsService>());
    return services;
}
    public static void TryAdd(this IServiceCollection collection, ServiceDescriptor descriptor)
     {
            if (!collection.Any((ServiceDescriptor d) => d.ServiceType == descriptor.ServiceType))
            {
                collection.Add(descriptor);
            }
     }

所以我们应该按照这个方法修改我们的AddMyLog方法。