循序渐进学.Net Core Web Api开发系列【11】:依赖注入
系列目录
本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi
一、概述
本篇介绍如何采用依赖注入的方式创建和使用对象,主要从应用层面进行描述,不涉及具体的内部原理。
二、演练
假设要做一个日志服务的类,它实现在控制台打印出带时间信息的日志信息。
首先定义该服务的接口与实现类。
public interface ILogService { void LogInfomation(string info); }</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> MyLogService : ILogService { </span><span style="color: #0000ff;">void</span> ILogService.LogInfomation(<span style="color: #0000ff;">string</span><span style="color: #000000;"> info) { Console.WriteLine($</span><span style="color: #800000;">"</span><span style="color: #800000;"> ==> MyLogService : {DateTime.Now.ToString()}:{info}</span><span style="color: #800000;">"</span><span style="color: #000000;">); } }</span></pre>
注册该服务
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddCors();<span style="color: #ff00ff;">services.AddSingleton</span></span><span style="color: #ff00ff;"><ILogService, MyLogService></span><span style="color: #000000;"><span style="color: #ff00ff;">();</span> }</span></pre>
注册成功,我们在Controller中使用该服务:
public class ArticleController : Controller {
private readonly ILogService _myLog;</span><span style="color: #0000ff;">public</span> ArticleController(<span style="color: #000000;"><span style="color: #ff00ff;">ILogService myLog</span>) { </span><span style="color: #000000;"> _myLog </span>=<span style="color: #000000;"> myLog; } [HttpGet(</span><span style="color: #800000;">"</span><span style="color: #800000;">logger</span><span style="color: #800000;">"</span><span style="color: #000000;">)] </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> TestLogger(<span style="color: #0000ff;">string</span><span style="color: #000000;"> logger) { _myLog.LogInfomation(</span><span style="color: #800000;">"</span><span style="color: #800000;">hahaha</span><span style="color: #800000;">"</span><span style="color: #000000;">); </span><span style="color: #0000ff;">return</span><span style="color: #000000;">; } }</span></pre>
简单分析一下:
1、首先通过services.AddSingleton方法向依赖注入容器登记注册MyLogService;
2、在构建Controller时,根据其构造函数类型遍历其输入参数,在依赖注入容器中找到该对象并作为实参传递给构造方法。
三、生命周期问题
注册一个服务,根据生命周期需要的不同,有下面三种方式:
1 2 3 4 5 | 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;</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> MyLogService() { _guid </span>=<span style="color: #000000;"> Guid.NewGuid(); } </span><span style="color: #0000ff;">void</span> ILogService.LogInfomation(<span style="color: #0000ff;">string</span><span style="color: #000000;"> info) { Console.WriteLine($</span><span style="color: #800000;">"</span><span style="color: #800000;"> ==> MyLogService : My Guid is :{_guid}</span><span style="color: #800000;">"</span><span style="color: #000000;">); Console.WriteLine($</span><span style="color: #800000;">"</span><span style="color: #800000;"> ==> MyLogService : {DateTime.Now.ToString()}:{info}</span><span style="color: #800000;">"</span><span style="color: #000000;">); } }</span></pre>
四、通过扩展方法注册服务
通过对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); }
五、几个问题
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();
</span><span style="color: #0000ff;">var</span> provider =<span style="color: #000000;"> services.BuildServiceProvider();
</span><span style="color: #0000ff;">var</span> servicesList = provider.GetServices< ILogService ><span style="color: #000000;">();
</span><span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> service <span style="color: #0000ff;">in</span><span style="color: #000000;"> servicesList)
{
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">service:</span><span style="color: #800000;">"</span> +<span style="color: #000000;"> service.ToString());
}</span></pre>
以上代码输出3条记录。
但下面的代码只输出一条记录:
services.AddCors();
services.AddCors();
services.AddCors();
</span><span style="color: #0000ff;">var</span> provider =<span style="color: #000000;"> services.BuildServiceProvider();
</span><span style="color: #0000ff;">var</span> servicesList = provider.GetServices<ICorsService><span style="color: #000000;">();
</span><span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> service <span style="color: #0000ff;">in</span><span style="color: #000000;"> servicesList)
{
Console.WriteLine(</span><span style="color: #800000;">"</span><span style="color: #800000;">service:</span><span style="color: #800000;">"</span> +<span style="color: #000000;"> service.ToString());
}</span></pre>
具体原因看一下源码就清楚了:
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方法。
</div>