依赖注入

本篇概述

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

1 演练

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

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

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>();

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

  • Singleton:单例服务,从当前服务容器中获取这个类型的实例永远是同一个实例;
  • Scoped:每个作用域生成周期内创建一个实例;
  • 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}");
    }
}

3 通过扩展方法注册服务

  通过对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);
}

4 几个问题

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

版权声明: 本文为智客工坊「seabluescn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

results matching ""

    No results matching ""