天天看點

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

ASP.NET 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直接給出紅色提示,需要傳入構造函數的參數,否則無法編譯過去。

當然我們可以直接new一個MyService來當做參數,但是很顯然這樣就失去了注入的那些好處了。

在ActionFilter中使用依賴注入

在ASP.NET Core的ActionFilter中使用依賴注入主要有兩種方式:

ServiceFilterAttribute

TypeFilterAttribute

使用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也可以使你的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";
    }           

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的構造器:

總結

ActionFilterAttribute的依賴注入可以通過ServiceFilterAttribute,TypeFilterAttribute來實作

ServiceFilterAttribute是通過DI容器來管理ActionFilterAttribute;TypeFilterAttribute則是通過一個工廠直接執行個體化,是以使用前不需要注冊到DI容器中。

IsReusable屬性可以實作類似單例的功能,但是運作時并不保證唯一單例。

TypeFilterAttribute的Arguments屬性可以作為參數清單。當執行個體化ActionFilterAttribute的時候如果構造器參數類型沒有在DI容器中注冊那麼會嘗試從Arguments清單中取。

Email:[email protected]

作者:Agile.Zhou(kklldog)

出處:

http://www.cnblogs.com/kklldog/

繼續閱讀