
ASP.NET Core 依赖注入(基础)[学习笔记]
环境准备
项目结构
服务类
GenericService.cs
public interface IGenericService<T>
{
}
public class GenericService<T> : IGenericService<T>
{
public T Data { get; private set; }
public GenericService(T data)
{
this.Data = data;
}
}
IService.cs
public interface IService
{
Guid GetId();
}
MyScopedService.cs
public interface IMyScopedService { }
public class MyScopedService : IMyScopedService
{
}
MySingletonService.cs
public interface IMySingletonService
{
}
public class MySingletonService : IMySingletonService
{
}
MyTransientService.cs
public interface IMyTransientService
{
}
public class MyTransientService : IMyTransientService
{
}
OrderService.cs
public interface IOrderService
{
}
public class OrderService : IOrderService
{
}
public class OrderServiceEx : IOrderService
{
}
控制器类
WeatherForecastController.cs
[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;
}
依赖注入
介绍
参考jesse大佬的:https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html
依赖:依赖于抽象,而不是具体的实现。
注入:注入体现的是一个IOC(控制反转的的思想)。容器:统一管理依赖的地方,大部分容器的内部实现方式,都是工厂模式+反射
IOC:我的理解是实例化不由自身来决定了,而是交给容器来进行实例了。举例:
Userinfo user = 容器.create(“user”) as Userinfo;
DI :通过容器实例对象时,给对象赋值(有意见请评论)
注册服务不同生命周期的服务
Startup.cs
#region 注册服务不同生命周期的服务
services.AddSingleton<IMySingletonService, MySingletonService>();
services.AddScoped<IMyScopedService, MyScopedService>();
services.AddTransient<IMyTransientService, MyTransientService>();
#endregion
注册方式 | 效果 |
---|---|
Singleton | 单例,永远都是那一个固定的实例进行注入 |
Transient | 注入的实例永远不同 |
Scoped | 请求的方法不同,注入的实例就不相同 |
结果
花式注册
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();
//});
尝试注册
//我们的服务已经注册过了,就不能再注册了
//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();
//}));
尝试注入
实现不同就尝试注册
移除和替换注册
//替换
services.Replace(ServiceDescriptor.Singleton<IOrderService, OrderServiceEx>());
//删除
services.RemoveAll<IOrderService>();
替换
移除
注册泛型模板
services.AddSingleton(typeof(IGenericService<>), typeof(GenericService<>));
Scoped变Singleton的问题
在一个需要注入Singleton
单例注入的服务中,如果该服务的注入是Scoped
,那么该服务的注入会变成Singleton
一样的生命周期,在规定内释放的时候无法进行释放。
解决这个的方式是使用ServiceProvider
的扩展BuildServiceProvider
方法,该方法对其注入有严格的验证,我们来分析一下该方法。
该扩展有3个重载方法;我们发现都是通过配置选项
ServiceProviderOptions
对服务范围进行的检验,我们接着来看ServiceProviderOptions
配置类中的属性
我们只看重要的两个属性
ValidateScopes
与ValidateOnBuild
。表示
属性 | 描述 |
---|---|
ValidateScopes |
当设置为True 时开启针对范围的检测。 |
ValidateOnBuild |
当设置为True 时检测构造是否能够提供服务。 |
ValidateScopes
属性的示例
public interface ITest { }
public interface IBoobar { }
public class Test : ITest { }
public class Boobar : IBoobar
{
public Boobar(ITest test)
{
_test = test;
}
public ITest _test { get; }
}
var services = new ServiceCollection();
// 创建单例ITest
services.AddSingleton<ITest, Test>();
// 添加范围注册
services.AddScoped<IBoobar, Boobar>();
var root = services.BuildServiceProvider(new ServiceProviderOptions
{
ValidateScopes = true
});
// 获取服务
root.GetService<IBoobar>();
我们可以看到它直接抛出InvalidOperationException
异常,无法解析…;当我们反转一下注册的方式,它是以单例模式Transient
为主,如果需要提供的服务不是Transient
是Scoped
将会注册失败
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
属性的示例
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
只有一个私有构造,无法进行实例。
项目地址
简单的依赖注入的架构
课程推荐
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739


忘掉过去式
优秀![[哈哈]](https://tnblog.net/layui/images/face/2.gif)
是伍尚金哇_v
老哥,什么时候出个github使用的入门到正常提交项目日常的文章呢