之前梳理過依賴注入和控制反轉,總結來說,控制反轉是一種思想,依賴注入是一種設計模式,控制反轉的思想可以利用依賴注入的設計模式實現,反射是依賴注入實現過程的核心技術。這里不在詳述依賴注入、控制反轉和反射。本文的重心是梳理依賴注入設計模式在ASP.NET Core的應用。
一、ASP.NET Core依賴注入的原理
ASP.NET Core 支持依賴關系注入 (DI) 軟件設計模式,這是一種在類及其依賴關系之間實現控制反轉 (IoC) 的技術。上文中也提到利用DI要做的兩個功能是:
- 注冊服務
- 注入服務
那么在ASP.NET Core中是如何實現這兩個功能的呢?
1、注冊服務的實現
就從startup.cs中的ConfigureServices方法說起,先來看下定義:
public virtual void ConfigureServices (Microsoft.Extensions.DependencyInjection.IServiceCollection services);
這里涉及到一個概念IServiceCollection,先來看下IServiceCollection的命名空間和定義:
namespace Microsoft.Extensions.DependencyInjection { /// <summary> /// Specifies the contract for a collection of service descriptors. /// </summary> public interface IServiceCollection : IList<ServiceDescriptor> { } }
發現又涉及到一個概念ServiceDescriptor,這里不對ServiceDescriptor展開,總結來說,ServiceDescriptor對象用來描述一種服務,包括該服務的類型、實現和生存期。所以說IServiceCollection是為ServiceDescriptor集合指定協定,即IServiceCollection用來管理ServiceDescriptor集合。先來看下IServiceCollection的實現ServiceCollection:
namespace Microsoft.Extensions.DependencyInjection { /// <summary> /// Default implementation of <see cref="IServiceCollection"/>. /// </summary> public class ServiceCollection : IServiceCollection { private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>(); /// <inheritdoc /> public int Count => _descriptors.Count; /// <inheritdoc /> public bool IsReadOnly => false; /// <inheritdoc /> public ServiceDescriptor this[int index] { get { return _descriptors[index]; } set { _descriptors[index] = value; } } /// <inheritdoc /> public void Clear() { _descriptors.Clear(); } /// <inheritdoc /> public bool Contains(ServiceDescriptor item) { return _descriptors.Contains(item); } /// <inheritdoc /> public void CopyTo(ServiceDescriptor[] array, int arrayIndex) { _descriptors.CopyTo(array, arrayIndex); } /// <inheritdoc /> public bool Remove(ServiceDescriptor item) { return _descriptors.Remove(item); }
void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item) { _descriptors.Add(item); } /// <inheritdoc /> public int IndexOf(ServiceDescriptor item) { return _descriptors.IndexOf(item); } /// <inheritdoc /> public void Insert(int index, ServiceDescriptor item) { _descriptors.Insert(index, item); } /// <inheritdoc /> public void RemoveAt(int index) { _descriptors.RemoveAt(index); } } }
接下來解析下ServiceCollection代碼,代碼中定義了ServiceDescriptor的集合_descriptors :
private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
然后就是對ServiceDescriptor集合進行增刪改查相關的操作,因此我們可以認為ServiceDescriptor對象就是一個服務,ServiceDescriptor集合就是服務容器,IServiceCollection的實現類ServiceCollection提供了對服務容器進行注冊、移除等相關的管理。這里就算是完成了依賴注入需要完成的功能1:注冊服務。
2、注入服務的實現
上邊介紹了ASP.NET Core利用IServiceCollection和ServiceDescriptor完成了服務的注冊,那么服務的注入如何實現呢?如何在需要的地方能夠注入相應的服務呢?
IServiceCollection的實現類ServiceCollection提供了注冊服務的相關方法,那么有沒有提供服務注入的方法呢?先來看下ServiceCollection的擴展方法:
namespace Microsoft.Extensions.DependencyInjection { /// <summary> /// Extension methods for building a <see cref="ServiceProvider"/> from an <see cref="IServiceCollection"/>. /// </summary> public static class ServiceCollectionContainerBuilderExtensions { /// <summary> /// Creates a <see cref="ServiceProvider"/> containing services from the provided <see cref="IServiceCollection"/>. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> containing service descriptors.</param> /// <returns>The <see cref="ServiceProvider"/>.</returns> public static ServiceProvider BuildServiceProvider(this IServiceCollection services) { return BuildServiceProvider(services, ServiceProviderOptions.Default); } /// <summary> /// Creates a <see cref="ServiceProvider"/> containing services from the provided <see cref="IServiceCollection"/> /// optionally enabling scope validation. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> containing service descriptors.</param> /// <param name="validateScopes"> /// <c>true</c> to perform check verifying that scoped services never gets resolved from root provider; otherwise <c>false</c>. /// </param> /// <returns>The <see cref="ServiceProvider"/>.</returns> public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes) { return services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = validateScopes }); } /// <summary> /// Creates a <see cref="ServiceProvider"/> containing services from the provided <see cref="IServiceCollection"/> /// optionally enabling scope validation. /// </summary> /// <param name="services">The <see cref="IServiceCollection"/> containing service descriptors.</param> /// <param name="options"> /// Configures various service provider behaviors. /// </param> /// <returns>The <see cref="ServiceProvider"/>.</returns> public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } return new ServiceProvider(services, options); } } }
ServiceCollection擴展方法提供了構建IServiceProvider的方法,先來了解下IServiceProvider的定義:
namespace System { public interface IServiceProvider { object GetService(Type serviceType); } }
IServiceProvider提供給了一個根據類型獲取對象的功能。
讀到這里先來梳理下,算是承上啟下吧:上邊說道ServiceCollection提供了管理服務集合的方法,即注冊服務;ServiceCollection的擴展方法提供了構建IServiceProvider的方法,而IServiceProvider提供給了一個根據類型獲取對象的功能。
理論上說Asp.net core利用IServiceCollection、ServiceDescriptor和IServiceProvider實現了注冊服務和注入服務的功能。
那么這里有個疑問?IServiceProvider是如何實現注入服務的呢?接下來我們分析下IServiceProvider實現類ServiceProvider 的源碼:
namespace Microsoft.Extensions.DependencyInjection { /// <summary> /// The default IServiceProvider. /// </summary> public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback, IAsyncDisposable { private readonly IServiceProviderEngine _engine; private readonly CallSiteValidator _callSiteValidator; internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options) { IServiceProviderEngineCallback callback = null; if (options.ValidateScopes) { callback = this; _callSiteValidator = new CallSiteValidator(); } switch (options.Mode) { case ServiceProviderMode.Default: #if !NETCOREAPP _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); #else if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled")) { _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); } else { // Don't try to compile Expressions/IL if they are going to get interpreted _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); } #endif break; case ServiceProviderMode.Dynamic: _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); break; case ServiceProviderMode.Runtime: _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); break; #if IL_EMIT case ServiceProviderMode.ILEmit: _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback); break; #endif case ServiceProviderMode.Expressions: _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback); break; default: throw new NotSupportedException(nameof(options.Mode)); } if (options.ValidateOnBuild) { List<Exception> exceptions = null; foreach (var serviceDescriptor in serviceDescriptors) { try { _engine.ValidateService(serviceDescriptor); } catch (Exception e) { exceptions = exceptions ?? new List<Exception>(); exceptions.Add(e); } } if (exceptions != null) { throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray()); } } } /// <summary> /// Gets the service object of the specified type. /// </summary> /// <param name="serviceType">The type of the service to get.</param> /// <returns>The service that was produced.</returns> public object GetService(Type serviceType) => _engine.GetService(serviceType); /// <inheritdoc /> public void Dispose() { _engine.Dispose(); } void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite) { _callSiteValidator.ValidateCallSite(callSite); } void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope) { _callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope); } /// <inheritdoc/> public ValueTask DisposeAsync() { return _engine.DisposeAsync(); } } }
從上面代碼中我們看到ServiceProvider實現了GetService方法:
public object GetService(Type serviceType) => _engine.GetService(serviceType);
但是里面涉及到一個對象_engine,它是什么呢?我們接着分析_engine初始化的代碼:
internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options) { IServiceProviderEngineCallback callback = null; if (options.ValidateScopes) { callback = this; _callSiteValidator = new CallSiteValidator(); } switch (options.Mode) { case ServiceProviderMode.Default: #if !NETCOREAPP _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); #else if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled")) { _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); } else { // Don't try to compile Expressions/IL if they are going to get interpreted _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); } #endif break; case ServiceProviderMode.Dynamic: _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback); break; case ServiceProviderMode.Runtime: _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback); break; #if IL_EMIT case ServiceProviderMode.ILEmit: _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback); break; #endif case ServiceProviderMode.Expressions: _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback); break; default: throw new NotSupportedException(nameof(options.Mode)); } if (options.ValidateOnBuild) { List<Exception> exceptions = null; foreach (var serviceDescriptor in serviceDescriptors) { try { _engine.ValidateService(serviceDescriptor); } catch (Exception e) { exceptions = exceptions ?? new List<Exception>(); exceptions.Add(e); } } if (exceptions != null) { throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray()); } } }
從上面代碼可以看出_engine是一個與ServiceDescriptor關聯的IServiceProviderEngine對象。而且根據ServiceProviderMode枚舉內容的不同,_engine有不同的初始化方案,下面看下ServiceProviderMode的枚舉值:
namespace Microsoft.Extensions.DependencyInjection { internal enum ServiceProviderMode { Default, Dynamic, Runtime, Expressions, ILEmit } }
這里有個疑問:IServiceProviderEngine是什么?為什么_engine有不同的初始化方案?ServiceProviderMode中的方案都代表着什么?
為了解決上邊的疑問,接下來探究下IServiceProviderEngine的定義:
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal interface IServiceProviderEngine : IServiceProvider, IDisposable, IAsyncDisposable { IServiceScope RootScope { get; } void ValidateService(ServiceDescriptor descriptor); } }
好像看不出來什么,接下來看下IServiceProviderEngine的實現類ServiceProviderEngine:
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory { private readonly IServiceProviderEngineCallback _callback; private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor; private bool _disposed; protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) { _createServiceAccessor = CreateServiceAccessor; _callback = callback; Root = new ServiceProviderEngineScope(this); RuntimeResolver = new CallSiteRuntimeResolver(); CallSiteFactory = new CallSiteFactory(serviceDescriptors); CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite()); RealizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>(); } internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; } internal CallSiteFactory CallSiteFactory { get; } protected CallSiteRuntimeResolver RuntimeResolver { get; } public ServiceProviderEngineScope Root { get; } public IServiceScope RootScope => Root; public void ValidateService(ServiceDescriptor descriptor) { if (descriptor.ServiceType.IsGenericType && !descriptor.ServiceType.IsConstructedGenericType) { return; } try { var callSite = CallSiteFactory.GetCallSite(descriptor, new CallSiteChain()); if (callSite != null) { _callback?.OnCreate(callSite); } } catch (Exception e) { throw new InvalidOperationException($"Error while validating the service descriptor '{descriptor}': {e.Message}", e); } } public object GetService(Type serviceType) => GetService(serviceType, Root); protected abstract Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite); public void Dispose() { _disposed = true; Root.Dispose(); } public ValueTask DisposeAsync() { _disposed = true; return Root.DisposeAsync(); } internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) { if (_disposed) { ThrowHelper.ThrowObjectDisposedException(); } var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor); _callback?.OnResolve(serviceType, serviceProviderEngineScope); DependencyInjectionEventSource.Log.ServiceResolved(serviceType); return realizedService.Invoke(serviceProviderEngineScope); } public IServiceScope CreateScope() { if (_disposed) { ThrowHelper.ThrowObjectDisposedException(); } return new ServiceProviderEngineScope(this); } private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType) { var callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain()); if (callSite != null) { DependencyInjectionEventSource.Log.CallSiteBuilt(serviceType, callSite); _callback?.OnCreate(callSite); return RealizeService(callSite); } return _ => null; } } }
具體分析下上邊的代碼:
- ValidateService方法:在_engine初始化的代碼中(即ServiceProvider構造函數中)調用過,針對該方法,這里不做展開,知道是用來驗證服務是否符合規則的就可以了。
從上述代碼中看出,ServiceProviderEngine構造函數中調用了CreateServiceAccessor方法,在CreateServiceAccessor方法中又調用了RealizeService方法,而ServiceProviderEngine中的RealizeService方法是個抽象方法,具體的實現體現了_engine初始化的方案:
- DynamicServiceProviderEngine類:間接繼承了ServiceProviderEngine類,重寫了RealizeService方法。
- ILEmitServiceProviderEngine類:繼承了ServiceProviderEngine類,重寫了RealizeService方法。
- ExpressionsServiceProviderEngine類:繼承了ServiceProviderEngine類,重寫了RealizeService方法。
- RuntimeServiceProviderEngine類:繼承了ServiceProviderEngine類,重寫了RealizeService方法。
這四個類都重寫了RealizeService方法,但是他們的目的都是一樣的:編譯一個類型為Func <ServiceProvider,object>的委托,並被緩存起來服務於后續針對同一個類型的服務提供請求,該委托對象與對應服務類型之間的映射關系就保存在RealizedServices屬性中。簡單的說RealizeService方法就是將對注冊的服務做一個類型映射關系,然后將該關系保存在RealizedServices字典中,RealizedServices是一個ConcurrentDictionary字典對象。
現在又回歸到前面說到的ServiceProvider是如何實現注入服務的:先來看下RealizedServices的定義:
internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; }
RealizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>();
用於存儲類型與Func<ServiceProviderEngineScope, object>委托的關系,而ServiceProviderEngineScope繼承了IServiceProvider,並且ServiceProviderEngineScope類中實現了GetService方法:
public object GetService(Type serviceType) { if (_disposed) { ThrowHelper.ThrowObjectDisposedException(); } return Engine.GetService(serviceType, this); }
而Engine調用ServiceProviderEngine中GetService方法,進而從RealizedServices獲取相應的服務。
internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) { if (_disposed) { ThrowHelper.ThrowObjectDisposedException(); } var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor); _callback?.OnResolve(serviceType, serviceProviderEngineScope); DependencyInjectionEventSource.Log.ServiceResolved(serviceType); return realizedService.Invoke(serviceProviderEngineScope); }
二、ASP.NET Core中注冊服務
上邊仔細研讀了ASP.NET Core的源碼,並且分析了依賴注入的實現原理。接下來就是說下在應用層面上應該如何使用。在startup類中,可以在ConfigureServices方法中注冊服務:
public void ConfigureServices(IServiceCollection services)
1、IServiceCollection中的方法和擴展方法
下面可用於服務的注冊,即服務注冊的方法:
AddScoped,添加服務,服務實例的生命周期為Scoped。 AddTransient,添加服務,服務實例的生命周期為Transient(每次被使用都創建新對象)。 AddSingleton,添加服務,服務實例的生命周期為單例。 AddMvc,添加所有MVC所需的服務。 AddMvcCore,僅添加核心必要的MVC所需的服務。 AddControllers,添加啟用Controller 所需要的服務,不包括View和Pages所需要的服務。 AddControllersWithViews,添加啟用 Controller 以及 Razor 頁面所需要的服務。 AddRazorPages,添加 Razor Pages 所需要的服務。 AddAntiforgery,添加防止CSRF攻擊的服務。 AddAuthentication,添加啟用Authentication中間件所需的服務。 AddAuthenticationCore,添加啟用Authentication中間件所需的核心服務。 AddAuthorization,添加啟用Authorization中間件所需的服務。 AddAuthorizationCore,添加啟用Authorization中間件所需的核心服務。 AddAuthorizationPolicyEvaluator,添加 Authorization 策略評估服務。 AddCertificateForwarding,添加CertificateForwarding中間件所需的服務。 AddConnections,添加 http://ASP.NET Core Connection Handlers 所需的服務。 AddCors,添加CORS中間件 所需的服務。 AddDataProtection,添加 http://ASP.NET Core Data Protection 所需的服務。 AddDirectoryBrowser,添加 DirectoryBrowser 中間件所需的服務。 AddDistributedMemoryCache,添加分布式緩沖服務IDistributedCache,默認的實現將緩沖保存在內存中,要實現實際上的分布式緩沖你需要提供一個保存緩存的實現(Redis或數據庫,如AddStackExchangeRedisCache和AddDistributedSqlServerCache)。 AddHealthChecks,添加HealthChecks中間件所需的服務。 AddHostedService,添加宿主服務,如持續運行的服務。 AddHostFiltering,添加HostFiltering中間件所需的服務。 AddHsts,添加HSTS中間件所需的服務。 AddHttpClient,添加IHttpClientFactory服務用於獲取在服務器端發起Http請求的HttpClient對象。 AddHttpContextAccessor,添加Http上下文訪問器服務,在例如Controller里有HttpContext屬性的地方優先使用HttpContext,但如果在一個自定義的服務里你就需要IHttpContextAccessor服務來獲取Http上下文。 AddHttpsRedirection,為HttpsRedirection中間件添加所需的服務。 AddIdentity,添加默認的身份系統,並制定 Role和User類型。 AddIdentityCore,添加默認身份執行的核心部分,並制定User類型。 AddLocalization,添加本地化服務。 AddLogging,添加日志服務。 AddMemoryCache,添加非分布式的內存緩存服務。 AddOptions,添加 Option 服務。 AddResponseCaching,為ResponseCaching中間件添加所需的服務。 AddResponseCompression,為ResponseCompression中間件添加所需的服務。 AddRouting,添加Routing中間件所需的服務。 AddSignalR,添加SignalR所需的服務。 AddSignalRCore,添加SignalR所需的核心服務。 AddServerSideBlazor,添加 Server-Side Blazor所需的服務。 AddWebEncoders,添加 HtmlEncoder,JavaScriptEncoder,UrlEncoder 三個服務。
2、自定義的IServiceCollection擴展方法
這里不在詳述。
三、ASP.NET Core中注入服務
1、構造函數注入
(1)示例一:Serilog日志服務
自定義一個IHostBuilder擴展方法UseSeriLog,並注冊該服務,如下圖:
在ValuesController中注入該服務:
namespace CrmRedevelop.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { private readonly ILogger _logger; public ValuesController(ILogger logger) { _logger = logger; } // GET api/<ValuesController>/5 [HttpGet("{id}")] public string Get(int id) { _logger.Information("構造函數注入"); return "value"; } } }
看下效果:
(2)示例二:IHostEnvironment
namespace CrmRedevelop.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController1 : ControllerBase { private readonly IHostEnvironment _hostEnvironment; public ValuesController1(IHostEnvironment hostEnvironment) { _hostEnvironment = hostEnvironment; } // GET: api/<ValuesController> [HttpGet] public IEnumerable<string> Get() { var path = _hostEnvironment.ContentRootPath; return new string[] { path }; } } }
看下效果:
2、特性FromServices注入
FromServicesAttribute 允許將服務直接注入到操作方法,而無需使用構造函數注入。
(1)示例一:Serilog日志服務
namespace CrmRedevelop.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { // GET: api/<ValuesController> [HttpGet] public IEnumerable<string> Get([FromServices] ILogger logger) { logger.Information("利用特性FromServices注入服務"); return new string[] { "value1", "value2" }; } } }
查看下效果:
(2)示例二:IHostEnvironment
namespace CrmRedevelop.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController1 : ControllerBase { // GET: api/<ValuesController> [HttpGet] public IEnumerable<string> Get([FromServices] IHostEnvironment hostEnvironment) { var path = hostEnvironment.ContentRootPath; return new string[] { path }; } } }
看下效果:
3、利用IServiceProvider的擴展方法GetRequiredService來檢索服務
GetRequiredService是IServiceProvider的擴展方法,可以從System.IServiceProvider獲取類型為T的服務。
(1)示例一:Serilog日志服務
namespace CrmRedevelop.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { // POST api/<ValuesController> [HttpPost] public void Post([FromBody] string value) { var logger = HttpContext.RequestServices.GetRequiredService<ILogger>(); logger.Information("GetRequiredService來檢索服務"); } } }
看下效果:
(2)示例二:IHostEnvironment
namespace CrmRedevelop.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController1 : ControllerBase { // GET: api/<ValuesController> [HttpGet] public IEnumerable<string> Get() { var hostEnvironment = HttpContext.RequestServices.GetRequiredService<IHostEnvironment>(); var path=hostEnvironment.ContentRootPath; return new string[] { path }; } } }
四、ASP.NET Core中服務的生命周期
上邊介紹依賴注入在ASP.NET Core實現的時候,並沒有提到生命周期的知識,這里單獨梳理下,在ConfigureServices方法中的注冊服務的時候,Asp.NET Core都可以為每個服務提供三種服務生命周期:
-
Transient(暫時):每次請求都會創建一個新的實例。這種生命周期最適合輕量級,無狀態服務。
-
Scoped(作用域):在同一個作用域內只初始化一個實例 ,可以理解為每一個請求只創建一個實例,同一個請求會在一個作用域內。
-
Singleton(單例):整個應用程序生命周期以內只創建一個實例,后續每個請求都使用相同的實例。如果應用程序需要單例行為,建議讓服務容器管理服務的生命周期,而不是在自己的類中實現單例模式。
下面通過示例驗證下:
1、定義三個接口
public interface IUser { } public interface IAnimal { } public interface ITree { }
2、定義三個實現類
public class User : IUser { } public class Animal : IAnimal { } public class Tree : ITree { }
3、在startup中注冊三個服務
//演示生命周期 services.AddTransient<IUserService, UserService>(); services.AddScoped<IAnimalService, AnimalService>(); services.AddSingleton<ITreeService, TreeService>();
4、測試
namespace xx { [Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { private readonly IUserService _userService1; private readonly IUserService _userService2; private readonly IAnimalService _animalService1; private readonly IAnimalService _animalService2; private readonly ITreeService _treeService1; private readonly ITreeService _treeService2; public ValuesController1(IUserService userService1, IUserService userService2, IAnimalService animalService1, IAnimalService animalService2, ITreeService treeService1, ITreeService treeService2) { _userService1 = userService1; _userService2 = userService2; _animalService1 = animalService1; _animalService2 = animalService2; _treeService1 = treeService1; _treeService2 = treeService2; } // GET: api/<ValuesController> [HttpGet] public string Get() { var sb = new StringBuilder(); sb.Append("transient1:" + _userService1.GetHashCode() + "<br />"); sb.Append("transient2:" + _userService2.GetHashCode() + "<br />"); sb.Append("scope1:" + _animalService1.GetHashCode() + "<br />"); sb.Append("scope2:" + _animalService2.GetHashCode() + "<br />"); sb.Append("singleton1:" + _treeService1.GetHashCode() + "<br />"); sb.Append("singleton2:" + _treeService2.GetHashCode() + "<br />"); return sb.ToString(); } } }
第一次刷新:
transient1:12314717 transient2:30850230 scope1:13632691 scope2:13632691 singleton1:38865907 singleton2:38865907
第二次刷新:
transient1:2272647
transient2:56816183
scope1:11118446
scope2:11118446
singleton1:38865907
singleton2:38865907
因此:
-
transient類型的生命周期,每次使用都不一樣,不同的類或不同的方法使用都不一樣
-
scope類型的生命周期,在同一個請求內是一樣的
-
singleton類型的生命周期,每次請求都是一樣的
所以理解了生命周期的作用,在開發的時候就可以根據需要對不同的服務選擇不同的生命周期。