欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ASP.NET Core - 在ActionFilter中使用依赖注入

程序员文章站 2023-02-08 14:30:38
上次[ActionFilter引发的一个EF异常](https://www.cnblogs.com/kklldog/p/not-use-sync-in-actionfilter.html),本质上是对Core版本的ActionFilter的知识掌握不够牢固造成的,所以花了点时间仔细阅读了微软的官方文... ......

上次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直接给出红色提示,需要传入构造函数的参数,否则无法编译过去。
ASP.NET Core - 在ActionFilter中使用依赖注入
当然我们可以直接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中:
ASP.NET Core - 在ActionFilter中使用依赖注入

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 - 在ActionFilter中使用依赖注入
这里有一个重要提示, 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中:
ASP.NET Core - 在ActionFilter中使用依赖注入

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的构造器:
ASP.NET Core - 在ActionFilter中使用依赖注入

总结

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