ASP.NET Core 依賴注入(DI)

原文: ASP.NET Core 依賴注入(DI)

  ASP.NET Core的底層設計支持和使用依賴注入。ASP.NET Core 應用程序能夠利用內置的框架服務將服務注入到啓動類的方法中,而且應用程序服務也能夠配置注入。由ASP.NET Core 提供的默認服務容器提供了最小功能集,並非取代其餘容器。html

  1.淺談依賴注入設計模式

  依賴注入(Dependency injection,DI)是一種實現對象和依賴者之間鬆耦合的技術,將類用來執行其操做的這些對象以注入的方式提供給該類,而不是直接實例化依賴項或者使用靜態引用。通常狀況,類會經過構造函數聲明器2依賴關係,容許他們遵循顯示依賴原則。這種方法稱爲「構造函數注入」。api

  當類的設計使用DI思想時,他們的耦合更加鬆散,由於他們沒有對他們的合做者直接硬編碼的依賴。這遵循「依賴倒置原則」,其中指出,高層模塊不該該依賴於底層模塊:二者都依賴於抽象。cookie

  類要求在他們構造時向其提供抽象(一般是接口),而不是引用特定的實現。提取接口的依賴關係和提供接口的實現做爲參數也是「策略設計模式」的一個示例。框架

  當一個類被用來建立類及其相關的依賴關係時,這個成爲容器(containers),或者稱爲控制反轉(Inversion of Control, IoC)容器,或者依賴注入容器。容器本質上是一個工廠,負責提供向它請求的類型的實例。若是一個給定類型聲明它具備依賴關係,而且容器已經被配置爲其提供依賴關係,那麼它將把建立依賴關係做爲建立請求實例的一部分。除了建立對象的依賴關係外,容器一般還會管理應用程序中對象的生命週期。async

  ASP.NET Core 包含一個默認支持構造函數注入的簡單內置容器,ASP.NET 的容器指的是它管理的類型services,能夠在Startup類的ConfigureServices方法中配置內置容器的服務。ide

 

  2. 使用ASP.NET Core提供的服務函數

  Startup類的ConfigureServices方法負責定義應用程序將使用的服務,包括平臺自帶的功能,好比,Entity Framework Core 和 ASP.NET Core MVC。除了IServiceCollection提供的幾個服務以外,可使用一些擴展方法(AddDbContext,AddMvc,AddTransient等)向容器添加和註冊額外服務:  測試

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddDbContext<AccessManagementContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                providerOptions => providerOptions.EnableRetryOnFailure()));
            services.AddTransient<ICompanyServices, CompanyServices>();

        }

  ASP.NET Core 提供的功能和中間件,遵循約定使用一個單一的AddService擴展方法來註冊全部該功能所需的服務。ui

 

  3.註冊本身的服務

  咱們能夠按照 services.AddTransient<ICompanyServices, CompanyServices>(); 這種寫法註冊本身的服務。第一個範型類型表示將要從容器中請求的類型(一般是一個接口)。第二個範型類型表示將由容器實例化而且用於完成請求的具體類型。

  AddTransient 方法用於將抽象類型映射到爲每個須要它的對象分別實例化的具體服務。爲註冊的每個服務選擇合適的生命週期很重要,後面會介紹到。

  

  下面是示例是註冊本身的服務:

  1.接口

public interface IAccountServices
    {
        Task<List<AccountViewModel>> GetList();
    }

  2.實現類

public class AccountServices:IAccountServices
    {
        AccessManagementContext _context;
        public AccountServices(AccessManagementContext context)
        {
            _context = context;//在構造函數中注入
        }

        public async Task<List<Account>> GetList()
        {
            try
            {
                var query = _context.Account.ToListAsync();
                 return query ;
            }
            catch (Exception ex)
            {
                return null;
            }

        }
}

  3.在ConfigureServices中註冊自定義的服務和EF上下文AccessManagementContext 

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            services.AddDbContext<AccessManagementContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                providerOptions => providerOptions.EnableRetryOnFailure()));
            services.AddTransient<IAccountServices,AccountServices>();

        }

  4.在Controller構造函數中依賴注入

public class AccountController : Controller
    {
        private IAccountServices _accountServices;
        public AccountController(IAccountServices accountServices)
        {
            _accountServices = accountServices;
        }
        // GET: Account
        public async Task<ActionResult> Index()
        {
            var vms = await _accountServices.GetList();
            return View(vms);
        }

  

  4.服務的生命週期和註冊選項

   ASP.NET 服務生命週期:

      1.Transient 瞬時

        Transient 生命週期服務在他們每次請求時被建立。適合輕量級,無狀態的服務。

      2.Scoped 做用域

         Scoped生命週期在每次請求時建立一次。

      3.Singleton 單例

        Singleton 生命週期服務在它們第一次請求時建立,而且每一個後續請求使用相同的實例。

 

  服務能夠用多種方式在容器中註冊,除了以前的註冊方法,還能夠指定一個工廠,它將被用來建立須要的實例。後面會詳細介紹其餘的註冊方法。

  下面用一個簡單的示例介紹每一個生命週期:

  1.建立接口:

namespace MVCTest.Interfaces
{
    public interface IOperation
    {
        /// <summary>
        /// 惟一標識
        /// </summary>
        Guid OperationId { get;  }
    }

    public interface IOperationTransient: IOperation
    {
    }

    public interface IOperationScoped : IOperation
    {
    }

    public interface IOperationSingleton : IOperation
    {
    }

    public interface IOperationInstance : IOperation
    {
    }
}

  

  2.實現類

    /// <summary>
    /// 實現全部接口
    /// </summary>
    public class Operation: IOperation, IOperationTransient,
        IOperationScoped, IOperationSingleton, IOperationInstance
    {
        public Operation()
        {
            OperationId = Guid.NewGuid();
        }
        public Operation(Guid operationId)
        {
            if (operationId == null)
            {
                OperationId = Guid.NewGuid();
            }
            OperationId = operationId;
        }

        public Guid OperationId { get; }
    }

  3.註冊到容器

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IOperationTransient, Operation>();
            services.AddScoped<IOperationScoped, Operation>();
            services.AddSingleton<IOperationSingleton, Operation>();
            services.AddSingleton<IOperationInstance, Operation>();
            services.AddTransient<OperationServices, OperationServices>();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

  4.上面還註冊了 OperationServices ,用來測試單例模式(單例生命週期服務中全部請求使用第一次實例化的服務)和 做用域生命週期服務在每次請求時只建立一次,無論幾個地方用到實例

    public class OperationServices
    {
        public IOperationTransient OperationTransient { get;  }
        public IOperationScoped OperationScoped { get;  }
        public IOperationSingleton OperationSingleton { get;  }
        public IOperationInstance OperationInstance { get;  }

        public OperationServices(IOperationTransient operationTransient,
            IOperationScoped operationScoped,
            IOperationSingleton operationSingleton,
            IOperationInstance operationInstance)
        {
            OperationTransient = operationTransient;
            OperationScoped = operationScoped;
            OperationSingleton = operationSingleton;
            OperationInstance = operationInstance;
        }
    }

 

  5.在Controller中使用

    public class OperationController : Controller
    {
        public IOperationTransient OperationTransient { get; }
        public IOperationScoped OperationScoped { get; }
        public IOperationSingleton OperationSingleton { get; }
        public IOperationInstance OperationInstance { get; }
        public OperationServices _operationServices;

        public OperationController(IOperationTransient operationTransient,
            IOperationScoped operationScoped,
            IOperationSingleton operationSingleton,
            IOperationInstance operationInstance,
            OperationServices operationServices)
        {
            OperationTransient = operationTransient;
            OperationScoped = operationScoped;
            OperationSingleton = operationSingleton;
            OperationInstance = operationInstance;
            _operationServices = operationServices;
        }
        // GET: Operation
        public ActionResult Index()
        {
            ViewBag.OperationTransient = OperationTransient;
            ViewBag.OperationScoped = OperationScoped;
            ViewBag.OperationSingleton = OperationSingleton;
            ViewBag.OperationInstance = OperationInstance;
            ViewBag._operationServices = _operationServices;
            return View();
        }
}

  6.Index顯示

@{
    ViewData["Title"] = "Index";
}

<div>
    <h1>Controller Operations</h1>
    <h2>OperationTransient: @ViewBag.OperationTransient.OperationId</h2>
    <h2>OperationScoped: @ViewBag.OperationScoped.OperationId</h2>
    <h2>OperationSingleton: @ViewBag.OperationSingleton.OperationId</h2>
    <h2>OperationInstance: @ViewBag.OperationInstance.OperationId</h2>
</div>
<div>
    <h1>Services Operations</h1>
    <h2>OperationTransient: @ViewBag._operationServices.OperationTransient.OperationId</h2>
    <h2>OperationScoped: @ViewBag._operationServices.OperationScoped.OperationId</h2>
    <h2>OperationSingleton: @ViewBag._operationServices.OperationSingleton.OperationId</h2>
    <h2>OperationInstance: @ViewBag._operationServices.OperationInstance.OperationId</h2>
</div>

 

  7.運行結果

    

    能夠看到,單例生命週期服務每一次請求的標識同樣。做用域生命週期的服務,在一次請求中使用的同一個實例,第二次請求建立新的實例。

 

  5.請求服務

  來自HttpContext的一次ASP.NET 請求中,可用的服務是經過RequestServices集合公開的。

  請求服務將你配置的服務和請求描述爲應用程序的一部分。在子的對象指定依賴以後,這些知足要求的對象可經過查找RequestServices中對應的類型獲得,而不是ApplicationServices。

 

  6.設計依賴注入服務

  在自定義的服務中,避免使用靜態方法和直接實例化依賴的類型,而是經過依賴注入請求它。(New is Glue)

  若是類有太多的依賴關係被注入時,一般代表你的類試圖作的太多(違反了單一職責原則),須要轉移一些職責。

  一樣,Controller類應該重點關注UI,所以業務邏輯和數據訪問等細節應該在其餘類中。

 

  7.使用Autofac容器

  Autofac

相關文章
相關標籤/搜索