Asp.net core 2.0 官網(wǎng)搬運(yùn)系列-依賴注入(DI)

1. 什么是控制反轉(zhuǎn)(IoC)和依賴注入(DI)?

使用過Java Spring或者Angular的同學(xué)肯定不陌生,不知道的建議google一下。

Martin Fowler has written an extensive article on Inversion of Control Containers and the Dependency Injection Pattern. Microsoft Patterns and Practices also has a great description of Dependency Injection.

2. 使用構(gòu)造函數(shù)的DI

  • 構(gòu)造函數(shù)必須是public
  • 只能有一個(gè)構(gòu)造函數(shù)用來進(jìn)行所有的服務(wù)注冊(cè)
  • 構(gòu)造函數(shù)可以接受沒有DI的參數(shù),但此時(shí)參數(shù)必須有默認(rèn)值

3. core2.0 框架內(nèi)置的服務(wù):

Startup類中的ConfigureService方法定義應(yīng)用中所有的服務(wù)。public void ConfigureServices(IServiceCollection services)

在初始時(shí),IServiceCollection提供了一些內(nèi)置服務(wù):

Service Type Lifetime
Microsoft.AspNetCore.Hosting.IHostingEnvironment Singleton
Microsoft.Extensions.Logging.ILoggerFactory Singleton
Microsoft.Extensions.Logging.ILogger<T> Singleton
Microsoft.AspNetCore.Hosting.Builder.IApplicationBuilderFactory Transient
Microsoft.AspNetCore.Http.IHttpContextFactory Transient
Microsoft.Extensions.Options.IOptions<T> Singleton
System.Diagnostics.DiagnosticSource Singleton
System.Diagnostics.DiagnosticListener Singleton
Microsoft.AspNetCore.Hosting.IStartupFilter Transient
Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton
Microsoft.Extensions.Options.IConfigureOptions<T> Transient
Microsoft.AspNetCore.Hosting.Server.IServer Singleton
Microsoft.AspNetCore.Hosting.IStartup Singleton
Microsoft.AspNetCore.Hosting.IApplicationLifetime Singleton

我們可以添加額外的服務(wù):

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
}

ASP.NET中間件,如MVC等,使用“約定大于配置”原則,即AddServiceName的方式注冊(cè)服務(wù),如 AddMvc(),AddDbContext()

4. 注冊(cè)自己的服務(wù)

services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();

第一個(gè)泛型參數(shù)是在構(gòu)造函數(shù)中使用的類,一般情況下是一個(gè)接口;第二個(gè)參數(shù)是實(shí)例化出來的類,一般情況下是接口實(shí)現(xiàn)類。

AddTransient代表實(shí)例化類的生命周期:

  • Transient:服務(wù)在每次請(qǐng)求時(shí)被實(shí)例化,這種情況最適用于輕量的、無狀態(tài)的服務(wù)
  • Scoped:服務(wù)在相同請(qǐng)求調(diào)用時(shí)被實(shí)例化一次
  • Singleton:?jiǎn)卫?,整個(gè)應(yīng)用生命周期中只實(shí)例化一次。

為了解釋清楚上述概念,我們來看一個(gè)例子:

  1. 首先聲明服務(wù)接口:
using System;

namespace DependencyInjectionSample.Interfaces
{
    public interface IOperation
    {
        Guid OperationId { get; }
    }

    public interface IOperationTransient : IOperation
    {
    }
    public interface IOperationScoped : IOperation
    {
    }
    public interface IOperationSingleton : IOperation
    {
    }
    public interface IOperationSingletonInstance : IOperation
    {
    }
}
  1. 接口實(shí)現(xiàn)類Opreation,構(gòu)造函數(shù)接受一個(gè)Guid,若沒有提供則使用new Guid:
public class Operation
{
    public Guid _guid {get;private set;}
    public Operation(Guid guid)
    {
        _guid = guid==null?Guid.Empty:guid;
    }
}
  1. ConfigureServices中注冊(cè)服務(wù):
    services.AddScoped<ICharacterRepository, CharacterRepository>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
    services.AddTransient<OperationService, OperationService>();

額外注意上面的IOperationSingletonInstance, 它直接使用了一個(gè)空的GUID(全是0)實(shí)例化了Operation類,并將這個(gè)類進(jìn)行注冊(cè)。
同時(shí),還要注意OperationService, 這個(gè)類如下:

using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Services
{
    public class OperationService
    {
        public IOperationTransient TransientOperation { get; }
        public IOperationScoped ScopedOperation { get; }
        public IOperationSingleton SingletonOperation { get; }
        public IOperationSingletonInstance SingletonInstanceOperation { get; }

        public OperationService(IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance instanceOperation)
        {
            TransientOperation = transientOperation;
            ScopedOperation = scopedOperation;
            SingletonOperation = singletonOperation;
            SingletonInstanceOperation = instanceOperation;
        }
    }
}

這個(gè)類依賴于所有的接口,目的是為了看清楚究竟每個(gè)服務(wù)的實(shí)例化狀態(tài)

  1. 控制器:
using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionSample.Controllers
{
    public class OperationsController : Controller
    {
        private readonly OperationService _operationService;
        private readonly IOperationTransient _transientOperation;
        private readonly IOperationScoped _scopedOperation;
        private readonly IOperationSingleton _singletonOperation;
        private readonly IOperationSingletonInstance _singletonInstanceOperation;

        public OperationsController(OperationService operationService,
            IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance singletonInstanceOperation)
        {
            _operationService = operationService;
            _transientOperation = transientOperation;
            _scopedOperation = scopedOperation;
            _singletonOperation = singletonOperation;
            _singletonInstanceOperation = singletonInstanceOperation;
        }

        public IActionResult Index()
        {
            // viewbag contains controller-requested services
            ViewBag.Transient = _transientOperation;
            ViewBag.Scoped = _scopedOperation;
            ViewBag.Singleton = _singletonOperation;
            ViewBag.SingletonInstance = _singletonInstanceOperation;
            
            // operation service has its own requested services
            ViewBag.Service = _operationService;
            return View();
        }
    }
}
  1. 視圖(略)

  2. 結(jié)果:


    第一次請(qǐng)求

    第二次請(qǐng)求
  • 無論如何,Transient 每次都實(shí)例化新的(every controller and every service.)
  • Scoped:一次請(qǐng)求中總相同,不同請(qǐng)求中不同
  • singleton: 總是相同(不管你有沒有在ConfigureServices中注冊(cè)實(shí)例),也就是說,前面的
services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

總會(huì)得到相同結(jié)果

5. Request Services

HttpContext暴露RequestServices,即所有的服務(wù)集合:

this.HttpContext.RequestServices.

但是,不主張用這種方式使用服務(wù),而是應(yīng)該使用DI的方法以實(shí)現(xiàn)解耦。

6. 服務(wù)設(shè)計(jì)原則

  • 建議先看看 S.O.L.I.D

  • 遵守SRP:the Single Responsibility Principle.單一職責(zé)原則

  • 比如操作數(shù)據(jù)庫時(shí),在controller中注入DbContext進(jìn)行直接操作是可以的。但是使用repository層對(duì)數(shù)據(jù)庫代碼進(jìn)行統(tǒng)一歸放往往是一個(gè)更好的主意---這樣,當(dāng)DB層變化時(shí),代碼往往比較好改。

  • Dispose和IDisposable接口

// Services implement IDisposable:
public class Service1 : IDisposable {}
public class Service2 : IDisposable {}
public class Service3 : IDisposable {}

public interface ISomeService {}
public class SomeServiceImplementation : ISomeService, IDisposable {}


public void ConfigureServices(IServiceCollection services)
{
    // container will create the instance(s) of these types and will dispose them
    services.AddScoped<Service1>();
    services.AddSingleton<Service2>();
    services.AddSingleton<ISomeService>(sp => new SomeServiceImplementation());

    // container did not create instance so it will NOT dispose it
    services.AddSingleton<Service3>(new Service3());
    services.AddSingleton(new Service3());
}
  • DI is for objects that have complex dependencies. Controllers, services, adapters, and repositories are all examples of objects that might be added to DI.

  • Avoid storing data and configuration directly in DI. For example, a user's shopping cart shouldn't typically be added to the services container. Configuration should use the options pattern. Similarly, avoid "data holder" objects that only exist to allow access to some other object. It's better to request the actual item needed via DI, if possible.

  • Avoid static access to services.

  • Avoid service location in your application code.

  • Avoid static access to HttpContext.
    Remember, dependency injection is an alternative to static/global object access patterns. You will not be able to realize the benefits of DI if you mix it with static object access.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容