core控制器属性注⼊的⽤处_全⾯理解ASP.NETCore依赖注⼊DI在.NET Core⾥⾯被提到了⼀个⾮常重要的位置, 这篇⽂章主要再给⼤家普及⼀下关于依赖注⼊的概念,⾝边有⼯作六七年的同事还个东西搞不清楚。另外再介绍⼀下.NET  Core的DI实现以及对实例⽣命周期的管理(这个是经常⾯试会问到的问题)。最后再给⼤家简单介绍⼀下在控制台以及Mvc下如何使⽤DI,以及如何把默认的Service Container 替换成Autofac。
⼀、什么是依赖注⼊
1.1 依赖
1.2 什么注⼊
为什么反转
何为容器
⼆、.NET Core DI
2.1 实例的注册
2.2 实例⽣命周期之单例
2.3 实例⽣命周期之Tranisent
2.4 实例⽣命周期之Scoped
三、DI在ASP.NET Core中的应⽤
3.1 在Startup类中初始化
3.2 Controller中使⽤
3.3 View中使⽤
3.4 通过HttpContext来获取
四、如何替换其它的Ioc容器
⼀、什么是依赖注⼊(Denpendency Injection)
这也是个⽼⾝常谈的问题,到底依赖注⼊是什么? 为什么要⽤它? 初学者特别容易对控制反转IOC(Iversion of Control),DI等概念搞晕。
1.1依赖
当⼀个类需要另⼀个类协作来完成⼯作的时候就产⽣了依赖。⽐如我们在AccountController这个控制器需要完成和⽤户相关的注册、登录等事情。其中的登录我们由EF结合Idnetity来完成,所以我们封装了⼀个EFLoginService。这⾥AccountController就有⼀个ILoginService的依赖。
这⾥有⼀个设计原则:依赖于抽象,⽽不是具体的实现。所以我们给EFLoginService定义了⼀个接⼝,抽象了LoginService的⾏为。
1.2 什么是注⼊
注⼊体现的是⼀个IOC(控制反转的的思想)。在反转之前 ,我们先看看正转。
AccountController⾃⼰来实例化需要的依赖。
private ILoginService _loginService;
public AccountController()
{
_loginService = new EFLoginService()
}
⼤师说,这样不好。你不应该⾃⼰创建它,⽽是应该由你的调⽤者给你。于是你通过构造函数让外界把这两个依赖传给你。
public
AccountController(ILoginService loginService)
{
_loginService = loginService;
}
把依赖的创建丢给其它⼈,⾃⼰只负责使⽤,其它⼈丢给你依赖的这个过程理解为注⼊。
1.3 为什么要反转?
为了在业务变化的时候尽少改动代码可能造成的问题。
⽐如我们现在要把从EF中去验证登录改为从Redis去读,于是我们加了⼀个 RedisLoginService。这个时候我们只需要在原来注⼊的地⽅改⼀下就可以了。
public
AccountController(ILoginService loginService)
{
_loginService = loginService;
}
// ⽤Redis来替换原来的EF登录 var controller = new AccountController(new RedisLoginService()); controller.Login(userName, password);
1.4 何为容器
上⾯我们在使⽤AccountController的时候,我们⾃⼰通过代码创建了⼀个ILoggingServce的实例。想象⼀下,⼀个系统中如果有100个这样的地⽅,我们是不是要在100个地⽅做这样的事情? 控制是反转了,
依赖的创建也移交到了外部。现在的问题是依赖太多,我们需要⼀个地⽅统⼀管理系统中所有的依赖,容器诞⽣了。
容器负责两件事情:
绑定服务与实例之间的关系
获取实例,并对实例进⾏管理(创建与销毁)
⼆、.NET Core DI
2.1 实例的注册
前⾯讲清楚DI和Ioc的关键概念之后,我们先来看看在控制台中对.NET Core DI的应⽤。在.NET Core中DI的核⼼分为两个组件:IServiceCollection和 IServiceProvider。
IServiceCollection 负责注册
IServiceProvider 负责提供实例
通过默认的 ServiceCollection(在Microsoft.Extensions.DependencyInjection命名空间下)有三个⽅法:
var serviceCollection = new ServiceCollection()
.AddTransient()
.AddSingleton()
.AddScoped();
这三个⽅法都是将我们的实例注册进去,只不过实例的⽣命周期不⼀样。什么时候⽣命周期我们下⼀节接着讲。
ServiceCollection的默认实现是提供⼀个ServiceDescriptor的List
public interface IServiceCollection : IList
{
}
我们上⾯的AddTransient、AddSignletone和Scoped⽅法是IServiceCollection的扩展⽅法, 都是往这个List⾥⾯添加ServiceDescriptor。
private static IServiceCollection Add(
IServiceCollection collection,
Type serviceType,
Type implementationType,
ServiceLifetime lifetime)
{
var descriptor =
new ServiceDescriptor(serviceType, implementationType, lifetime);
collection.Add(descriptor);
return collection;
}
2.2 实例的⽣命周期之单例
我们上⾯看到了,.NET Core DI 为我们提供的实例⽣命周其包括三种:
Transient: 每⼀次GetService都会创建⼀个新的实例
Scoped:  在同⼀个Scope内只初始化⼀个实例 ,可以理解为( 每⼀个request级别只创建⼀个实例,同⼀个http request会在⼀个scope内)
Singleton :整个应⽤程序⽣命周期以内只创建⼀个实例
对应了Microsoft.Extensions.DependencyInjection.ServiceLifetime的三个枚举值
public enum ServiceLifetime
{
Singleton,
Scoped,
Transient
}
为了⼤家能够更好的理解这个⽣命周期的概念我们做⼀个测试:
定义⼀个最基本的IOperation⾥⾯有⼀个 OperationId的属性,IOperationSingleton也是⼀样,只不过是另外⼀个接⼝。
public interface IOperation
{
Guid OperationId { get; }
}
public interface IOperationSingleton : IOperation { }
public interface IOperationTransient : IOperation{}
public interface IOperationScoped : IOperation{}
mvc和三层架构的理解
我们的 Operation实现很简单,可以在构造函数中传⼊⼀个Guid进⾏赋值,如果没有的话则⾃已New⼀个 Guid。public class Operation :
IOperationSingleton,
IOperationTransient,
IOperationScoped
{
private Guid _guid;
public Operation() {
_guid = Guid.NewGuid();
}
public Operation(Guid guid)
{
_guid = guid;
}
public Guid OperationId => _guid;
}
在程序内我们可以多次调⽤ServiceProvider的GetService⽅法,获取到的都是同⼀个实例。
var services = new ServiceCollection();
// 默认构造
services.AddSingleton();
// ⾃定义传⼊Guid空值
services.AddSingleton(
new Operation(Guid.Empty));
/
/ ⾃定义传⼊⼀个New的Guid
services.AddSingleton (
new Operation(Guid.NewGuid()));
var provider = services.BuildServiceProvider();
// 输出singletone1的Guid
var singletone1 = provider.GetService();
Console.WriteLine($"signletone1: {singletone1.OperationId}");
// 输出singletone2的Guid
var singletone2 = provider.GetService();
Console.WriteLine($"signletone2: {singletone2.OperationId}");
Console.WriteLine($"singletone1 == singletone2 ? : { singletone1 == singletone2 }");
我们对IOperationSingleton注册了三次,最后获取两次,⼤家要注意到我们获取到的始终都是我们最后⼀次注册的那个给了⼀个Guid的实例,前⾯的会被覆盖。
2.3 实例⽣命周期之Tranisent
这次我们获取到的IOperationTransient为两个不同的实例。
2.4 实例⽣命周期之Scoped
.NET Core⼈IServiceProvider提供CreateScope产⽣⼀个新的ServiceProvider范围,在这个范围下的Scope标注的实例将只会是同⼀个实例。换句话来说:⽤Scope注册的对象,在同⼀个ServiceProvider的 Scope下相当于单例。
同样我们先分别注册IOperationScoped、IOperationTransient和IOperationSingletone 这三个实例,⽤对应的Scoped、Transient、和Singleton⽣命周期。
var services = new ServiceCollection()
.AddScoped()
.AddTransient()
.AddSingleton();
接下来我们⽤ServiceProvider.CreateScope⽅法创建⼀个Scope
var provider = services.BuildServiceProvider();
using (var scope1 = provider.CreateScope())
{
var p = scope1.ServiceProvider;
var scopeobj1 = p.GetService();
var transient1 = p.GetService();
var singleton1 = p.GetService();
var scopeobj2 = p.GetService();
var transient2 = p.GetService();
var singleton2 = p.GetService();
Console.WriteLine(
$"scope1: { scopeobj1.OperationId }," +
$"transient1: {transient1.OperationId}, " +

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。