• ASP.NET Core


    上次ActionFilter引发的一个EF异常,本质上是对Core版本的ActionFilter的知识掌握不够牢固造成的,所以花了点时间仔细阅读了微软的官方文档。发现除了IActionFilter、IAsyncActionFilter的问题,还有一个就是依赖注入在ActionFilter上的使用也是需要注意的地方。
    当我们的ActionFilter需要使用某个Service的时候,我们一般会通过构造函数注入。
    演示一下,首先自定义一个ActionFilter,通过构造函数注入IMyService:

        public interface IMyService
        {
            string GetServiceName(); 
        }
    
        public class MyService : IMyService
        {
            public MyService ()
            {
                Console.WriteLine("Service {0} created .", GetServiceName());
            }
    
            public string GetServiceName()
            {
                return "MyService";
            }
        }
    
        public class FilterInjectAttribute: ActionFilterAttribute
        {
            public FilterInjectAttribute(IMyService myService)
            {
                if (myService == null)
                {
                    throw new ArgumentNullException("myService");
                }
    
                Console.WriteLine("Service {0} was injected .", myService.GetServiceName());
            }
        }
    

    但是我们在使用Attribute的时候VS直接给出红色提示,需要传入构造函数的参数,否则无法编译过去。

    当然我们可以直接new一个MyService来当做参数,但是很显然这样就失去了注入的那些好处了。

    在ActionFilter中使用依赖注入

    在ASP.NET Core的ActionFilter中使用依赖注入主要有两种方式:

    1. ServiceFilterAttribute
    2. TypeFilterAttribute

    ServiceFilterAttribute

    使用ServiceFilterAttribute可以使你的ActionFilter完成依赖注入。其实就是把你要用的ActionFilter本身注册为一个Service注册到DI容器中。通过ServiceFilter从容器中检索你的ActionFilter,并且注入到需要的地方。所以第一步就是要注册你的ActionFilter:

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddScoped<IMyService,MyService>();
                services.AddScoped(typeof(FilterInjectAttribute));
    
                services.AddControllers();
                services.AddRazorPages();
            }
    

    然后新建一个Controller,在Action上使用ServiceFilter:

            [ServiceFilter(typeof(FilterInjectAttribute))]
            public string DI()
            {
                Console.WriteLine("HomeController method DI running .");
    
                return "DI";
            }
    

    运行一下,在浏览器里访问下对应的path,可以看到MyService已经注入到FilterInjectAttribute中:

    ServiceFilterAttribute的IsReusable属性:

    ServiceFilter有一个属性叫IsReusable。从字面意思也很好理解,就是是否可重用的意思。显而易见如果这个属性设置为True,那么多个请求就会复用这个ActionFilter,这就有点像是单例的意思了。

            [ServiceFilter(typeof(FilterInjectAttribute), IsReusable = true)]
            public string DI()
            {
                Console.WriteLine("HomeController method DI running .");
    
                return "DI";
            }
    

    运行一下,多次在浏览器中访问对应的action的path,可以看到FilterInjectAttribute的构造函数只会执行一次。

    这里有一个重要提示, ASP.NET Core runtime 并不保证这个filter是真正的单例。所以不要试图使用这个属性来实现单例,并且业务系统依赖这个单例。

    TypeFilterAttribute

    使用TypeFilterAttribute也可以使你的ActionFilter完成依赖注入。它跟ServiceFilterAttribute差不多,但是使用TypeFilterAttribute注入的ActionFilter并不从DI容器中查找,而是直接通过Microsoft.Extensions.DependencyInjection.ObjectFactory来实例化对象。所以我们的FilterInjectAttribute不需要提前注册到DI容器中。
    首先注释掉FilterInjectAttribute的注册代码:

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddScoped<IMyService,MyService>();
    
                //services.AddScoped(typeof(FilterInjectAttribute));
    
                services.AddControllers();
                services.AddRazorPages();
            }
    

    改用TypeFilterAttribute:

            [TypeFilter(typeof(FilterInjectAttribute))]
            public string DI()
            {
                Console.WriteLine("HomeController method DI running .");
    
                return "DI";
            }
    

    运行一下,在浏览器里访问下对应的path,可以看到MyService已经注入到FilterInjectAttribute中:

    TypeFilterAttribute的IsReusable属性:

    跟上面的ServiceFilter一样,ASP.NET Core runtime 并不保证这个filter是真正的单例,这里就不多啰嗦了。

    TypeFilterAttribute的Arguments属性:

    Arguments参数是TypeFilterAttribute跟ServiceFilterAttribute的一个重要区别,ServiceFilterAttribute并没有这属性。Arguments类型为object数组。通过TypeFilterAttribute实例化的ActionFilter,如果它的构造器中的参数类型在DI容器中找不到,会继续在Arguments参数列表里按顺序获取。
    改一下FilterInjectAttribute构造器多加入2个参数,并且保证这2个参数无法从DI中取到:

        public class FilterInjectAttribute: ActionFilterAttribute
        {
            public FilterInjectAttribute(string arg1, IMyService myService, string arg2)
            {
                if (myService == null)
                {
                    throw new ArgumentNullException("myService");
                }
    
                Console.WriteLine("Service {0} was injected .", myService.GetServiceName());
                Console.WriteLine("arg1 is {0} .", arg1);
                Console.WriteLine("arg2 is {0} .", arg2);
    
                Console.WriteLine("FilterInjectAttribute was created .");
            }
        }
    

    在使用的时候传入两个参数:

            [TypeFilter(typeof(FilterInjectAttribute), Arguments  = new object[] { "HAHA", "HOHO" })]
            public string DI()
            {
                Console.WriteLine("HomeController method DI running .");
    
                return "DI";
            }
    

    运行一下看到两个参数被传入了FilterInjectAttribute的构造器:

    总结

    1. ActionFilterAttribute的依赖注入可以通过ServiceFilterAttribute,TypeFilterAttribute来实现
    2. ServiceFilterAttribute是通过DI容器来管理ActionFilterAttribute;TypeFilterAttribute则是通过一个工厂直接实例化,所以使用前不需要注册到DI容器中。
    3. IsReusable属性可以实现类似单例的功能,但是运行时并不保证唯一单例。
    4. TypeFilterAttribute的Arguments属性可以作为参数列表。当实例化ActionFilterAttribute的时候如果构造器参数类型没有在DI容器中注册那么会尝试从Arguments列表中取。

    关注我的公众号一起玩转技术

  • 相关阅读:
    非常强大的table根据表头排序,点击表头名称,对其内容排序
    处理字符集中的算式问题
    java基础复习:final,static,以及String类
    try,catch,finally含return时的执行顺序及丢失的伪例
    系统 触发器
    某个表按某个列分组且按另一个列排序
    XML读取
    硬软件交互原理
    TcpClient 读写流
    C# Socket的TCP通讯 异步 (2015-11-07 10:07:19)转载▼
  • 原文地址:https://www.cnblogs.com/kklldog/p/di-in-core-actionfilter.html
Copyright © 2020-2023  润新知