ASP.NET Core 奇淫技巧之伪属性注入

原文:ASP.NET Core 奇淫技巧之伪属性注入

一.前言

开局先唠嗑一下,许久未曾更新博客,一直在调整自己的状态,去年是我的本命年,或许是应验了本命年的多灾多难,过得十分不顺,不论是生活上还是工作上。还好当我度过了所谓的本命年后,许多事情都在慢慢变好,我将会开始恢复更新博客,争取恢复到以前的速度上(因为工作比较忙,所以这个过程可能需要一段时间)。

二.关于属性注入

说到属性注入,我们就不得不提一下 DI(Dependency Injection),即依赖注入,用过 ASP.NET Core 的同学相信对这个词不会陌生。ASP.NET Core 自带了一个IOC容器,且程序运行也是基于这个容器建立起来的,在 Startup 里的 ConfigureServices 方法就是向容器注册类型。最直白的讲,我们在 ASP.NET Core 中,想使用某个类型的时候可以不用自己去 new,可以由容器通过构造方法来注入具体的实现类型,而我们一般在构造方法上定义的依赖类型都是接口,而不是去依赖具体的实现,这里就体现了 SOLID 原则中的依赖倒置原则(DIP)。这也是IOC(Inversion of Control),即控制反转,不直接依赖具体实现,将依赖交给容器去控制。上述几者是具有一定的关联关系的,DIP 是一种软件设计原则,IOC 是 DIP 的具体实现方式,DI 是 IOC 的一种实现方式。

在依赖注入时,我们最常用的便是通过构造方法注入,还有另一种方式那便是属性注入

关于属性注入,如果在网上搜索,大部分内容都是不推荐使用,或者说慎重使用的,因为属性注入会造成类型的依赖关系隐藏,测试不友好等,我也同意这种说法,属性注入可以使用,但是要谨慎,不能盲目使用。我的原则:在封装框架(搭架子)时可以使用,但不能大范围使用,只有必须使用属性注入来达到效果的地方才会使用,用来提高使用框架时的编码效率,来达到一些便利,脱离框架层面,编写业务代码时,不得使用。

在 ASP.NET Core 中,自带的容器是不支持属性注入的,但是可以通过替换容器,如:Autofac 等来实现。今天我分享的方法不是使用替换容器,而是通过几行代码来实现属性注入的效果,我称为“伪属性注入”。

三.属性注入解决的痛点

以下介绍的痛点是我在实际编码过程中遇到的一些,如果还有其他的,欢迎在评论和我交流

我所遇到的痛点,我归纳为三条:

1.减少常用的类型的重复注入代码,使构造方法看起来更为简洁,提高阅读性。

2.减少或消除因构造方法注入造成子类继承后的 base 调用链。

3.并非是满足第一条或第二条就需要使用属性注入来解决,只有当第一、二条发生的情况到达一定的数量。

第一条:

以日志 ILogger<T> 为例,我们在 Controller 或者 应用服务层(Application Service)等编写业务的地方可能会常用,那么我们可能会在大部分的 Controller 或者 Application Service 的构造方法里写一句注入,例:

这里只是以日志来举例,我们还能遇到和日志这种相同的类型,每个 Controller 等都要注入一堆这种共同的类型,代码编写起来也比较麻烦,如果多了以后还影响代码阅读。

有何解决办法,那就是定义一个基类,然后通过属性提供给子类,以 Controller 为例:

第二条:

在上面的Controller基类注入 ILogger,然后设置了 Logger 属性,这样子类就可以使用 Logger 属性来使用日志。

这样做每次都要调用 base 将依赖对象传递给基类,如果继承关系有多层,将会造成更大的影响。

注意:本文演示只以日志来举例,如果只有一个ILogger我觉得还可以忍受,实际情况中并非只有一个,比如本地化等等。博主不提倡有上面演示情况的就用属性注入,当到达一定数量才使用,比如在 Controller 或者应用服务这种数量多的对象以及当这些对象需要的共同的注入类型达到一定数量。

四. 伪属性注入核心思想

依托于 ASP.NET Core 自带的容器,在 Resolve Service 时,为需要“属性注入”的属性进行赋值,可以使用 自带容器提供的 ImplementationFactory 来实现。

五. 为 Controller 实现伪属性注入

Controller 的实现较为特殊,Controller 默认是不会通过自带容器来 Resolve&Activate 的,是通过MVC自身管理的,但是微软提供了这样的方法:

services.AddControllers().AddControllersAsServices();

可以通过调用 AddControllersAsServices() 方法来让 Controller 使用自带容器,其主要源代码如下

根据第四小节的思想,我们需要 Controller Resolve 时,来对属性进行赋值,那么我们需要改造 Controller 激活器。

  1. 定义 Controller 基类

  1. Controller 继承基类

  1. 改造 Controller 激活器

可以看到我们改造的代码也就几行。

  1. 替换默认 Controller 激活器
services.AddControllers().AddControllersAsServices();
services.Replace(ServiceDescriptor.Transient<IControllerActivator, XcServiceBasedControllerActivator>()); //替换默认 Controller 激活器
  1. 运行测试

测试正常,如需其他属性的“属性注入”,参考日志这样做就行了。

六. 为 Application Service 实现伪属性注入

只是以 Application Service 来作为讲解,同理可举一反三到其他地方。Application Service 属于领域驱动分层架构中的一层,如不了解,可自行查找资料。

  1. 定义应用服务基类接口
public interface IAppService
{
    ILogger Logger { get; set; }
}

public class AppService:IAppService
{
    public ILogger Logger { get; set; }
}
  1. 定义具体服务,以 User 服务为例
public interface IUserAppService:IAppService
{
    void Create();
}

public class UserAppService : AppService,IUserAppService
{
    public void Create()
    {
        Logger.LogInformation("来自 Application Service 的日志");
    }
}
  1. 定义特殊的注册服务的方法,以便实现 Resolve 为 Logger 赋值
public static class ServiceExtensions
{
    public static IServiceCollection AddApplicationService<TService, TImpl>(this IServiceCollection services) where TService:IAppService where TImpl:AppService
    {
        services.AddApplicationService(typeof(TService), typeof(TImpl));
        return services;
    }
    // 可以反射程序集调用此方法实现批量自动注册应用服务
    public static IServiceCollection AddApplicationService(this IServiceCollection services, Type serviceType,Type implType)
    {
        services.AddTransient(serviceType, sp =>
        {
            //获取服务实现的实例
            var implInstance = ActivatorUtilities.CreateInstance(sp, implType); ;

            if (implInstance is AppService obj)
            {
                //为 Logger 赋值
                obj.Logger= sp.GetRequiredService<ILoggerFactory>().CreateLogger(implType);
            }

            return implInstance;
        });
        return services;
    }
  1. 注册测试服务

  1. Controller 注入测试服务

  1. 运行测试

七.结束

其实到本文写完,我都在想,要不要封装一个组件,发布到 Nuget 来方便的使用文中我所描述的“伪属性注入”,最后反复想了想,还是觉得不做。如果要使用完全的属性注入可以替换使用第三方容器,本文所述旨在不想引入第三方容器,且想在部分地方来达到属性注入的效果,因为属性注入这个东西也不推荐大范围使用。

本文来源于我在工作中的一些灵感总结,我在看 ControllerActivator 源码时的突发奇想,最近工作虽然忙,但是知识确实积攒了不少,在后面与大家一一分享。

姊妹篇:ASP.NET Core 奇淫技巧之动态WebApi

原文地址:https://www.cnblogs.com/lonelyxmas/p/12623763.html

时间: 2024-11-05 06:29:37

ASP.NET Core 奇淫技巧之伪属性注入的相关文章

[asp.net mvc 奇淫巧技] 04 - 你真的会用Action的模型绑定吗?

在QQ群或者一些程序的交流平台,经常会有人问:我怎么传一个数组在Action中接收.我传的数组为什么Action的model中接收不到.或者我在ajax的data中设置了一些数组,为什么后台还是接收不了.还有一些怎么传送一个复杂的对象或者Action怎么接收一个复杂的对象等等这些问题.或者有些人遇到复杂的对象或者数组直接就传送个json字符串,然后在Action中把json字符串转成model对象,当然这也是一种做法,但也许不是最优的做法. 一.需求 按照如图的数据格式,传入到Action,用一

[asp.net mvc 奇淫巧技] 06 - 也许你的项目同一个用户的请求都是同步的

一.感慨 很久前看到一篇博客中有句话大致的意思是:“asp.net 程序性能低下的主要原因是开发人员技术参差不齐”,当时看到这句话不以为然,然而时间过的越久接触的.net 开发人员越多就越认同这句话:特别最近发现非常一个成熟的项目中有些问题非常非常影响性能,最终影响的是用户体验,借此给大家分享一下关于asp.net中一个小小的点,但对项目有很大的性能提升:以前觉得自己接触的项目少小,然后接触的项目越多,越大就会越发现,同样的问题依旧存在: 二.先从最简单的asp.net mvc例子说起 1.Co

C之奇淫技巧——宏的妙用

一.宏列表 当遇到这样的问题的时候: 有一个标记变量,其中的每个位代表相应的含义.我们需要提供一组函数来访问设置这些位,但是对于每个标记位的操作函数都是相似的.若有32个位,难道要搞32套相似的操作函数么? 你也许会说,用一套操作函数,根据传入的参数来判断对哪个位操作.这样固然可行,但是 ①不够直观.例如访问Movable标记位,对于用户来说,is Movable()是很自然的方式,而我们只能提供这样的接口isFlag(Movable) ②扩展性差.若以后增加删改标记位,则需要更改isFlag等

优化DP的奇淫技巧

DP是搞OI不可不学的算法.一些丧心病狂的出题人不满足于裸的DP,一定要加上优化才能A掉. 故下面记录一些优化DP的奇淫技巧. OJ 1326 裸的状态方程很好推. f[i]=max(f[j]+sum[i]-sum[j]-100*I) (1<=j<i&&f[j]>=100*i) 然后把无关于j的提出来. f[i]=max(f[j]-sum[j])+sum[i]-100*i; 好的,现在只需要把在O(1)的时间内求出max(f[j]-sum[j])就是坠吼得. 考虑两个决策

NGINX的奇淫技巧 —— 6. IF实现数学比较功能 (1)

NGINX的奇淫技巧 —— 6. IF实现数学比较功能 (1) ARGUS 1月13日 发布 推荐 0 推荐 收藏 3 收藏,839 浏览 nginx的if支持=.!= 逻辑比较, 但不支持if中 <.<.>=.<= 比较.本示例使用了set-misc-nginx-module location = /test/ { default_type html; set_random $a 0 9; #$a 随机 从0-9取 if ( $a <= 4 ){ #$a 如果 < 4

12个实用的 Javascript 奇淫技巧

这里分享12个实用的 Javascript 奇淫技巧.JavaScript自1995年诞生以来已过去了16个年头,如今全世界无数的网页在依靠她完成各种关键任务,JavaScript曾在Tiobe发布的编程语言排行榜中排到了第8名,紧随C#,JavaScript从过去装饰性的一种脚本语言转变为主流的编程语言,人们用它来开发更大更复杂的程序. 1. 取整同时转成数值型: '10.567890'|0 结果: 10 '10.567890'^0 结果: 10 -2.23456789|0 结果: -2 ~~

C++奇淫技巧,程序员为啥天天学继承与派生,真的有这么难吗

继承与派生 1.1继承与派生的概念 在C++中,可重用性是通过继承这一机制来实现的.所谓继承,就是在一个已存在的类的基础上建立一个新的类.已存在的类称为基类,新建立的类成为派生类.(与对象的复制做区别)一个新类从已有的类那里获得其已有特性,这种现象称为类的继承. C++奇淫技巧,程序员为啥天天学继承与派生,真的有这么难吗派生类继承了基类的所有数据成员和成员函数,并可以对成员作必要的增加或调整.创一个小群,供大家学习交流聊天如果有对学C++方面有什么疑惑问题的,或者有什么想说的想聊的大家可以一起交

ASP.NET Core 2.2 基础知识(一) 依赖注入

原文:ASP.NET Core 2.2 基础知识(一) 依赖注入 依赖: 类A用到了类B,我们就说类A依赖类B.如果一个类没有任何地方使用到,那这个类基本上可以删掉了. public class Test { private MyDependency md = new MyDependency(); public void Print() { md.Print(); } } public class MyDependency { public void Print() { Console.Wri

ASP.NET Core中使用自定义MVC过滤器属性的依赖注入

原文:ASP.NET Core中使用自定义MVC过滤器属性的依赖注入 除了将自己的中间件添加到ASP.NET MVC Core应用程序管道之外,您还可以使用自定义MVC过滤器属性来控制响应,并有选择地将它们应用于整个控制器或控制器操作. ASP.NET Core中常用的MVC过滤器之一是  ExceptionFilterAttribute,用于处理Wep API应用程序中的错误响应.它很容易实现,开发人员和我在ASP.NET Core中使用MVC过滤器属性所面临的问题是访问Startup.cs类