Asp.NETCore讓FromServices回來


 

起因

這兩天,我忽然有點懷念 Asp.NET MVC 5 之前的時代,原因是我看到項目里面有這么一段代碼(其實不止一段,幾乎每個 Controller 都是)

    [Route("home")] [ApiController] public class HomeController : ControllerBase { private readonly IConfiguration configuration; private readonly IHostingEnvironment environment; private readonly CarService carService; private readonly PostServices postServices; private readonly TokenService tokenService; private readonly TopicService topicService; private readonly UserService userService; public HomeController(IConfiguration configuration, IHostingEnvironment environment, CarService carService, PostServices postServices, TokenService tokenService, TopicService topicService, UserService userService) { this.configuration = configuration; this.environment = environment; this.carService = carService; this.postServices = postServices; this.tokenService = tokenService; this.topicService = topicService; this.userService = userService; } [HttpGet("index")] public ActionResult<string> Index() { return "Hello world!"; } }

在構造函數里面聲明了一堆依賴注入的實例,外面還得聲明相應的接收字段,使用代碼克隆掃描,零零散散的充斥在各個 Controller 的構造函數中。在 Asp.NET MVC 5 之前,我們可以把上面的代碼簡化為下面的形式:

    [Route("home")] [ApiController] public class HomeController : ControllerBase { [FromServices] public IConfiguration Configuration { get; set; } [FromServices] public IHostingEnvironment Environment { get; set; } [FromServices] public CarService CarService { get; set; } [FromServices] public PostServices PostServices { get; set; } [FromServices] public TokenService TokenService { get; set; } [FromServices] public TopicService TopicService { get; set; } [FromServices] public UserService UserService { get; set; } public HomeController() { } [HttpGet("index")] public ActionResult<string> Index() { return "Hello world!"; } }

但是,在 .NETCore 中,上面的這斷代碼是會報錯的,原因就是特性:FromServicesAttribute 只能應用於 AttributeTargets.Parameter,導航到 FromServicesAttribute 查看源碼

namespace Microsoft.AspNetCore.Mvc { /// <summary> /// Specifies that an action parameter should be bound using the request services. /// </summary> /// <example> /// In this example an implementation of IProductModelRequestService is registered as a service. /// Then in the GetProduct action, the parameter is bound to an instance of IProductModelRequestService /// which is resolved from the request services. /// /// <code> /// [HttpGet] /// public ProductModel GetProduct([FromServices] IProductModelRequestService productModelRequest) /// { /// return productModelRequest.Value; /// } /// </code> /// </example> [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] public class FromServicesAttribute : Attribute, IBindingSourceMetadata { /// <inheritdoc /> public BindingSource BindingSource => BindingSource.Services; } }

那么問題來了,AttributeUsage 是什么時候移除了 AttributeTargets.Property 呢?答案是:2015年11月17日,是一個叫做 Pranav K 的哥們革了 FromServiceAttribute 的命,下面是他的代碼提交記錄

Limit [FromServices] to apply only to parameters
https://github.com/aspnet/Mvc/commit/2a89caed05a1bc9f06d32e15d984cd21598ab6fb

這哥們的 Commit Message 很簡潔:限制 FromServices 僅作用於 parameters 。高手過招,人狠話不多,刀刀致命!從此,廣大 .NETCore 開發者告別了屬性注入。經過我不懈努力的搜索后,發現其實在 Pranav K 提交代碼兩天后,他居然自己開了一個 Issue,你說氣人不?

關於廢除 FromServices 的討論
https://github.com/aspnet/Mvc/issues/3578

在這個貼子里面,許多開發者表達了自己的不滿,我還看到了有人像我一樣,表達了自己想要一個簡潔的構造函數的這樣朴素的請求;但是,對於屬性注入可能導致濫用的問題也產生了激烈的討論,還有屬性注入要求成員必須標記為 public 這些硬性要求,不得不說,這個帖子成功的引起了人們的注意,但是很明顯,作者不打算修改 FromServices 支持屬性注入。

自己動手,豐衣足食

沒關系,官方沒有自帶的話,我們自己動手做一個也是一樣的效果,在此之前,我們還應該關注另外一種從 service 中獲取實例的方式,就是常見的通過 HttpContext 請求上下文獲取服務實例的方式:

 var obj = HttpContext.RequestServices.GetService(typeof(Type));

上面的這種方式,其實是反模式的,官方也建議盡量避免使用,說完了廢話,就自動動手擼一個屬性注入特性類:PropertyFromServiceAttribute

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class PropertyFromServiceAttribute : Attribute, IBindingSourceMetadata { public BindingSource BindingSource => BindingSource.Services; }

沒有多余的代碼,就是標記為 AttributeTargets.Property 即可

應用到類成員
    [Route("home")] [ApiController] public class HomeController : ControllerBase { [PropertyFromService] public IConfiguration Configuration { get; set; } [PropertyFromService] public IHostingEnvironment Environment { get; set; } [PropertyFromService] public CarService CarService { get; set; } [PropertyFromService] public PostServices PostServices { get; set; } [PropertyFromService] public TokenService TokenService { get; set; } [PropertyFromService] public TopicService TopicService { get; set; } [PropertyFromService] public UserService UserService { get; set; } public HomeController() { } [HttpGet("index")] public ActionResult<string> Index() { return "Hello world!"; } }

請大聲的回答,上面的代碼是不是非常的干凈整潔!但是,像上面這樣使用屬性注入有一個小問題,在對象未初始化之前,該屬性為 null,意味着在類的構造函數中,該成員變量不可用,不過不要緊,這點小問題完全可用通過在構造函數中注入解決;更重要的是,並非每個實例都需要在構造函數中使用,是吧。

示例代碼

托管在 Github 上了 https://github.com/lianggx/Examples/tree/master/Ron.DI

** 如果你喜歡這篇文章,請給我點贊,讓更多同學可以看到,筆芯~

 

原文:https://www.cnblogs.com/viter/archive/2019/06/26/11085318.html


免責聲明!

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



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