这里只介绍依赖注入最基本的用法和示例
首先定义最基础的服务接口和服务实现类
代码1
public interface IOrderService
{
string Id { get; }
}
public class OrderService : IOrderService, IDisposable
{
public OrderService() => Id = Guid.NewGuid().ToString();
public string Id { get; private set; }
public void Dispose()
{
Console.WriteLine($"type is base, id is {Id}");
}
}
public class EcomOrderService : IOrderService, IDisposable
{
public EcomOrderService() => Id = DateTime.Now.ToString("yyyyMMddHHmmssfff");
public string Id { get; private set; }
public void Dispose()
{
Console.WriteLine($"type is ecom, id is {Id}");
}
}
执行服务的代码1
代码2
[HttpGet("")]
public void Get([FromServices] IOrderService orderService1, [FromServices] IOrderService orderService2)
{
Console.WriteLine($"orderService1 id is :{orderService1.Id},traceid:{HttpContext.TraceIdentifier}");
Console.WriteLine($"orderService2 id is :{orderService2.Id},traceid:{HttpContext.TraceIdentifier}");
}
关闭应用代码
代码3
[HttpGet("shutdown")]
public void Shutdown([FromServices] Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime) => applicationLifetime.StopApplication();
单例模式
services.AddSingleton(typeof(IOrderService), typeof(OrderService));
我们执行多此请求后可以看到所显示的id都是相同的,说明实例只存在一份。
从上面的代码中我们可以看到OrderService
是实现了IDispose
接口的,意味着在类释放的时候会执行Dispose
方法,从上面截图中我们没有看到该方法执行,说明我们的类并没有被释放掉。
当我们应用关闭的时候,OrderService
的IDispose
方法被执行,说明该类只有在应用程序关闭的时候才被释放掉。
作用域模式
作用域模式是会保证我们的一次请求作用域里,实例只会保持一份
services.AddScoped(typeof(IOrderService), typeof(OrderService));
我们执行多此请求后可以看到每一次请求中orderService1
和orderService2
中的id都是一样,但是新的请求却存在变化,同时每次请求结束之后就会触发OrderService
的Dispose
方法,而且只有一次,说明我们的每次Http请求是一次作用域,在Http请求结束时候会释放注入为Scoped
的对象
瞬态模式
services.AddTransient(typeof(IOrderService), typeof(OrderService));
瞬态模式表示对象每次从DI中获取中获取实例都是新的一份。
从上面的图中我们可以看到,在每一次Http请求中我们获取两份OrderSerivce
实例,但是这两份实例的Id
都不一样,说明我们每一次获取的OrderService
都是新的实例。而且在Http请求结束之后会发现出发了两次Dispose
方法,说明实例会在Http请求结束后进行释放。
后进式服务
当然这个后进式服务是个人自己取名得
在上面的代码中我们定义了一个服务接口IOrderService
,但是却存在两个实现类OrderService
和EcomService
,如果我们将两个实现类都通过AddXXX
方式进行注入,那在使用的时候会是哪个?
services.AddScoped(typeof(IOrderService), typeof(OrderService));
services.AddScoped(typeof(IOrderService), typeof(EcomOrderService));
然后执行服务代码1
的结果是:
可以看到我们的服务实例是第二个注入的服务实例即:EcomOrderService
,所以通过AddXXX
方式注入相同服务接口的是取的后进式的,即取最后注入的服务实例。
另外需要注意的是我们的两次服务注入其实都是成功了,我们可以使用[FromServices]IEnumerable<IOrderService>
的方式可以获取针对IOrderService
的所有服务实例。
代码4
public void GetMore([FromServices] IEnumerable<IOrderService> orderServices)
{
foreach (var orderService in orderServices)
{
Console.WriteLine($"orderService id is :{orderService.Id},traceid:{HttpContext.TraceIdentifier}");
}
}
运行的结果是:
从上图中可以看到IEnumerable<IOrderService>
对象中拥有的服务实例数和我们注入的服务实例数是一致的
前进式服务
既然能够出现后进式服务,那么肯定会出现前进式服务,这个我们可以使用TryAddXXX
方式注入相同的服务接口
services.TryAddScoped(typeof(IOrderService), typeof(OrderService));
services.TryAddScoped(typeof(IOrderService), typeof(EcomOrderService));
那按照这种方式注入得服务是否在IEnumerable<IOrderService>
拿到多个?
可以看到这里只有一个服务实例存在,意味着通过TryAddXXX
方式针对同一个服务接口,只会存在一个服务实例。
当然我们将后面得一个实例服务注入成其他生命周期也是一样的,比如我们第一个注入的是TryAddScoped
,第二个不管是TryAddScoped
还是TryAddSingleton
都不会注入成功
多服务实例注入
我们可以通过TryAddEnumerable
方法注入同一个服务接口不同实现实例,要注意的是如果给定的实现实例已经被注入了则不会重复注入
services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IOrderService), typeof(OrderService)));
services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IOrderService), typeof(EcomOrderService)));
services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IOrderService), typeof(OrderService)));
我们注入了三个服务实例,但是第一个和第三个是相同的,执行代码4得到的结果是:
可以从图中看到只有两个实例服务
而这种方式注入的多实现服务实例我们获取默认的实例也是获取最后一个注入的(即上面说的后进式)
生命周期的管理
这里我们讨论的是实现了IDisopose
接口的服务类
我们的服务在注入DI容器的时候,容器会帮助我们对服务的实例进行一个生命周期的管理,但是这里是有个条件的:我们的注入的服务必须是容器帮我们进行的实例化才能让容器进行回收释放。
第一个场景:先实例化再讲实例化的对象注入(因为是单例模式,所以如果容器需要释放的话会在应用程序结束的时候进行)
var orderService = new OrderService();
services.AddSingleton<IOrderService>(orderService);
第二个场景:我们服务注入时通过给定的工厂方法创建实例
services.AddSingleton<IOrderService>(sp => new OrderService());
后记
上述的TryAddXXX
方法必须要引用命名空间Microsoft.Extensions.DependencyInjection.Extensions
才能使用