• 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

    ** 如果你喜欢这篇文章,请给我点赞,让更多同学可以看到,笔芯~

  • 相关阅读:
    C语言寒假大作战01
    C语言I作业12—学期总结
    C语言I博客作业11
    C语言I博客作业10
    C语言I博客作业09
    C语言I作业07:第十二周作业
    团队作业6--复审与事后分析
    Alpha阶段项目复审
    事后诸葛亮
    团队作业5——测试与发布(Alpha版本)
  • 原文地址:https://www.cnblogs.com/viter/p/11085318.html
Copyright © 2020-2023  润新知