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

系列目录

循序渐进学.Net Core Web Api开发系列目录

 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi

 

一、概述

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

 

二、演练

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

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

   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等也是向容器注入服务。

        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方法。

 

posted @ 2018-07-17 16:50  seabluescn  阅读(2000)  评论(0编辑  收藏  举报