這兩天,我突然有點懷念 Asp.NET MVC 5 以前的時代,緣由是我看到項目裏面有這麼一段代碼(其實不止一段,幾乎每一個 Controller 都是)git
[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 以前,咱們能夠把上面的代碼簡化爲下面的形式:github
[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 查看源碼app
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/2a89caed05a1bc9f06d32e15d984cd21598ab6fbpost
這哥們的 Commit Message 很簡潔:限制 FromServices 僅做用於 parameters 。高手過招,人狠話很少,刀刀致命!今後,廣大 .NETCore 開發者告別了屬性注入。通過我不懈努力的搜索後,發現其實在 Pranav K 提交代碼兩天後,他竟然本身開了一個 Issue,你說氣人不?this
關於廢除 FromServices 的討論
https://github.com/aspnet/Mvc/issues/3578spa
在這個貼子裏面,許多開發者表達了本身的不滿,我還看到了有人像我同樣,表達了本身想要一個簡潔的構造函數的這樣樸素的請求;可是,對於屬性注入可能致使濫用的問題也產生了激烈的討論,還有屬性注入要求成員必須標記爲 public 這些硬性要求,不得不說,這個帖子成功的引發了人們的注意,可是很明顯,做者不打算修改 FromServices 支持屬性注入。code
不要緊,官方沒有自帶的話,咱們本身動手作一個也是同樣的效果,在此以前,咱們還應該關注另一種從 service 中獲取實例的方式,就是常見的經過 HttpContext 請求上下文獲取服務實例的方式:對象
var obj = HttpContext.RequestServices.GetService(typeof(Type));
上面的這種方式,實際上是反模式的,官方也建議儘可能避免使用,說完了廢話,就自動動手擼一個屬性注入特性類:PropertyFromServiceAttributetoken
[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
** 若是你喜歡這篇文章,請給我點贊,讓更多同窗能夠看到,筆芯~