ASP.NET Core 依赖注入(基础)[学习笔记] 电脑版发表于:2020/5/8 10:21 ![.netcore](https://img.tnblog.net/arcimg/hb/c857299a86d84ee7b26d181a31e58234.jpg ".netcore") >#ASP.NET Core 依赖注入(基础)[学习笔记] [TOC] <br/> 环境准备 ------------ <br/> >###项目结构 ![项目结构](https://img.tnblog.net/arcimg/hb/47dce22bf65747918a15d5b6c00b5b0b.png "项目结构") >###服务类 <br/> ><font style="color:#2ecc71;font-weight:bold;">GenericService.cs</font> ```csharp public interface IGenericService<T> { } public class GenericService<T> : IGenericService<T> { public T Data { get; private set; } public GenericService(T data) { this.Data = data; } } ``` ><font style="color:#2ecc71;font-weight:bold;">IService.cs</font> ```csharp public interface IService { Guid GetId(); } ``` ><font style="color:#2ecc71;font-weight:bold;">MyScopedService.cs</font> ```csharp public interface IMyScopedService { } public class MyScopedService : IMyScopedService { } ``` ><font style="color:#2ecc71;font-weight:bold;">MySingletonService.cs</font> ```csharp public interface IMySingletonService { } public class MySingletonService : IMySingletonService { } ``` ><font style="color:#2ecc71;font-weight:bold;">MyTransientService.cs</font> ```csharp public interface IMyTransientService { } public class MyTransientService : IMyTransientService { } ``` ><font style="color:#2ecc71;font-weight:bold;">OrderService.cs</font> ```csharp public interface IOrderService { } public class OrderService : IOrderService { } public class OrderServiceEx : IOrderService { } ``` >###控制器类 ><font style="color:#2ecc71;font-weight:bold;">WeatherForecastController.cs</font> ```csharp [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet("GetService")] public int GetService([FromServices]IMySingletonService singleton1, [FromServices]IMySingletonService singleton2, [FromServices]IMyTransientService transient1, [FromServices]IMyTransientService transient2, [FromServices]IMyScopedService scoped1, [FromServices]IMyScopedService scoped2) { Console.WriteLine($"singleton1:{singleton1.GetHashCode()}"); Console.WriteLine($"singleton2:{singleton2.GetHashCode()}"); Console.WriteLine($"transient1:{transient1.GetHashCode()}"); Console.WriteLine($"transient2:{transient2.GetHashCode()}"); Console.WriteLine($"scoped1:{scoped1.GetHashCode()}"); Console.WriteLine($"scoped2:{scoped2.GetHashCode()}"); Console.WriteLine($"========GetService 请求结束======="); return 1; } [HttpGet("GetService2")] public int GetService2([FromServices]IMySingletonService singleton1, [FromServices]IMySingletonService singleton2, [FromServices]IMyTransientService transient1, [FromServices]IMyTransientService transient2, [FromServices]IMyScopedService scoped1, [FromServices]IMyScopedService scoped2) { Console.WriteLine($"singleton1:{singleton1.GetHashCode()}"); Console.WriteLine($"singleton2:{singleton2.GetHashCode()}"); Console.WriteLine($"transient1:{transient1.GetHashCode()}"); Console.WriteLine($"transient2:{transient2.GetHashCode()}"); Console.WriteLine($"scoped1:{scoped1.GetHashCode()}"); Console.WriteLine($"scoped2:{scoped2.GetHashCode()}"); Console.WriteLine($"========GetService 2 请求结束======="); return 1; } /// <summary> /// 获取 Service /// </summary> /// <param name="services"></param> /// <returns></returns> [HttpGet("GetServiceList")] public int GetServiceList([FromServices]IEnumerable<IOrderService> services) { foreach (var item in services) { Console.WriteLine($"Get Servic instances:{item.ToString()}:{item.GetHashCode()}"); } return 1; } ``` <br/> 依赖注入 ------------ <br/> >###介绍 <br/> >参考jesse大佬的:https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html 依赖:依赖于抽象,而不是具体的实现。 注入:注入体现的是一个IOC(控制反转的的思想)。 >>容器:统一管理依赖的地方,大部分容器的内部实现方式,都是工厂模式+反射 IOC:我的理解是实例化不由自身来决定了,而是交给容器来进行实例了。举例: Userinfo user = 容器.create("user") as Userinfo; DI :通过容器实例对象时,给对象赋值 >(有意见请评论) <br/> >###注册服务不同生命周期的服务 <br/> ><font style="color:#2ecc71;font-weight:bold;">Startup.cs</font> ```csharp #region 注册服务不同生命周期的服务 services.AddSingleton<IMySingletonService, MySingletonService>(); services.AddScoped<IMyScopedService, MyScopedService>(); services.AddTransient<IMyTransientService, MyTransientService>(); #endregion ``` | 注册方式 | 效果 | | ------------ | ------------ | | Singleton | 单例,永远都是那一个固定的实例进行注入 | | Transient | 注入的实例永远不同 | | Scoped | 请求的方法不同,注入的实例就不相同 | >结果 ![生命周期](https://img.tnblog.net/arcimg/hb/449cded1218448d4bd5a9af65b429372.png "生命周期") <br/> >###花式注册 ```csharp services.AddSingleton<IOrderService>(new OrderService()); //直接注入实例 //services.AddSingleton<IOrderService, OrderServiceEx>(); //services.AddSingleton<IOrderService, OrderService>(); //services.AddSingleton<IOrderService, OrderService>(); //工厂模式 //services.AddSingleton<IOrderService>(serviceProvider => //{ // return new OrderServiceEx(); //}); //services.AddScoped<IOrderService>(serviceProvider => //{ // return new OrderServiceEx(); //}); ``` <br/> >###尝试注册 ```csharp //我们的服务已经注册过了,就不能再注册了 //services.TryAddSingleton<IOrderService, OrderServiceEx>(); //相同类型的服务接口,如果实现不同则可以注册进去 //services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrderService, OrderServiceEx>()); //services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrderService, OrderServiceEx>()); //services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrderService>(new OrderServiceEx())); //services.TryAddEnumerable(ServiceDescriptor.Singleton<IOrderService>(p => //{ // return new OrderServiceEx(); //})); ``` >尝试注入 ![尝试注入](https://img.tnblog.net/arcimg/hb/f58b2322a9c041d88bb6dce1bdc6c597.png "尝试注入") >实现不同就尝试注册 ![实现不同就尝试注册](https://img.tnblog.net/arcimg/hb/34a7ec66a60445f5897f02d1ad20f412.png) >###移除和替换注册 ```csharp //替换 services.Replace(ServiceDescriptor.Singleton<IOrderService, OrderServiceEx>()); //删除 services.RemoveAll<IOrderService>(); ``` >替换 ![替换](https://img.tnblog.net/arcimg/hb/39cacc512ff541a6866e0686a60ec7b6.png "替换") >移除 ![移除](https://img.tnblog.net/arcimg/hb/54bfec3483374f57a036f9dc11c4906e.png "移除") >###注册泛型模板 ```csharp services.AddSingleton(typeof(IGenericService<>), typeof(GenericService<>)); ``` >###Scoped变Singleton的问题 tn>在一个需要注入`Singleton`单例注入的服务中,如果该服务的注入是`Scoped`,那么该服务的注入会变成`Singleton`一样的生命周期,在规定内释放的时候无法进行释放。 解决这个的方式是使用`ServiceProvider`的扩展`BuildServiceProvider`方法,该方法对其注入有严格的验证,我们来分析一下该方法。 ![](https://img.tnblog.net/arcimg/hb/e9159f2ba90d4caa824781919f64cba6.png) ![](https://img.tnblog.net/arcimg/hb/67158487dd3a4697a88b000824378abf.png) >该扩展有3个重载方法;我们发现都是通过配置选项`ServiceProviderOptions`对服务范围进行的检验,我们接着来看`ServiceProviderOptions`配置类中的属性 ![](https://img.tnblog.net/arcimg/hb/8e8fbf28c1be4c51848776f7e8738e1b.png) >我们只看重要的两个属性`ValidateScopes`与`ValidateOnBuild`。表示 | 属性 | 描述 | | ------------ | ------------ | | `ValidateScopes` | 当设置为`True`时开启针对范围的检测。 | | `ValidateOnBuild` | 当设置为`True`时检测构造是否能够提供服务。 | >`ValidateScopes`属性的示例 ```csharp public interface ITest { } public interface IBoobar { } public class Test : ITest { } public class Boobar : IBoobar { public Boobar(ITest test) { _test = test; } public ITest _test { get; } } ``` ```csharp var services = new ServiceCollection(); // 创建单例ITest services.AddSingleton<ITest, Test>(); // 添加范围注册 services.AddScoped<IBoobar, Boobar>(); var root = services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true }); // 获取服务 root.GetService<IBoobar>(); ``` ![](https://img.tnblog.net/arcimg/hb/922c46821d714fd6819c8e7488995674.png) tn>我们可以看到它直接抛出`InvalidOperationException`异常,无法解析...;当我们反转一下注册的方式,它是以单例模式`Transient`为主,如果需要提供的服务不是`Transient`是`Scoped`将会注册失败 ```csharp var services = new ServiceCollection(); // 创建单例ITest services.AddScoped<ITest, Test>(); // 添加范围注册 services.AddSingleton<IBoobar, Boobar>(); var root = services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true }); // 子容器获取ITest服务成功 root.CreateScope().ServiceProvider.GetService<ITest>(); // 子容器获取IBoobar服务失败 root.CreateScope().ServiceProvider.GetService<IBoobar>(); // 根容器获取失败 root.GetService<ITest>(); // 根容器获取失败 root.GetService<IBoobar>(); ``` >`ValidateOnBuild`属性的示例 ```csharp public class Boobar : IBoobar { private Boobar() { } } static void Main(string[] args) { var services = new ServiceCollection(); // 添加单例注册 services.AddSingleton<IBoobar, Boobar>(); var root = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true }); // 根容器获取失败 root.GetService<IBoobar>(); } ``` >在服务注册时,便报错了。因为`Boobar`只有一个私有构造,无法进行实例。 ![](https://img.tnblog.net/arcimg/hb/019f9a35d3dc422ab7255e294ff2878d.png) <br/> 项目地址 ------------ <br/> >GitHub: https://github.com/AiDaShi/GeekTimeLearning <br/> 简单的依赖注入的架构 ------------ <br/> ![](https://img.tnblog.net/arcimg/hb/3dd358b93a654f6c86b6b621373d2e93.png) <br/> 课程推荐 ------------ <br/> ![](https://img.tnblog.net/arcimg/hb/6bb0a226374d487bb01aa9c586a1a9ff.jpeg)