ASP.Net Core解讀依賴注入


之前梳理過依賴注入控制反轉,總結來說,控制反轉是一種思想,依賴注入是一種設計模式,控制反轉的思想可以利用依賴注入的設計模式實現,反射是依賴注入實現過程的核心技術。這里不在詳述依賴注入、控制反轉和反射。本文的重心是梳理依賴注入設計模式在ASP.NET Core的應用。

一、ASP.NET Core依賴注入的原理

ASP.NET Core 支持依賴關系注入 (DI) 軟件設計模式,這是一種在類及其依賴關系之間實現控制反轉 (IoC) 的技術。上文中也提到利用DI要做的兩個功能是:

  1. 注冊服務
  2. 注入服務

那么在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初始化的方案:

  1. DynamicServiceProviderEngine類:間接繼承了ServiceProviderEngine類,重寫了RealizeService方法。
  2. ILEmitServiceProviderEngine類:繼承了ServiceProviderEngine類,重寫了RealizeService方法。
  3. ExpressionsServiceProviderEngine類:繼承了ServiceProviderEngine類,重寫了RealizeService方法。
  4. 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

因此:

  1. transient類型的生命周期,每次使用都不一樣,不同的類或不同的方法使用都不一樣

  2. scope類型的生命周期,在同一個請求內是一樣的

  3. singleton類型的生命周期,每次請求都是一樣的 

所以理解了生命周期的作用,在開發的時候就可以根據需要對不同的服務選擇不同的生命周期。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM