web-dev-qa-db-ja.com

.Net Core ActionFilterAttributeで依存性注入を使用するにはどうすればよいですか?

AuthenticationRequiredAttributeクラス

public class AuthenticationRequiredAttribute : ActionFilterAttribute
{
    ILoginTokenKeyApi _loginTokenKeyApi;
    IMemoryCache _memoryCache;

    public AuthenticationRequiredAttribute(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;

        _loginTokenKeyApi = new LoginTokenKeyController(new UnitOfWork());
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var memory = _memoryCache.Get(Constants.KEYNAME_FOR_AUTHENTICATED_PAGES);

        string requestedPath = filterContext.HttpContext.Request.Path;

        string tokenKey = filterContext.HttpContext.Session.GetString("TokenKey")?.ToString();

        bool? isLoggedIn = _loginTokenKeyApi.IsLoggedInByTokenKey(tokenKey).Data;

        if (isLoggedIn == null ||
            !((bool)isLoggedIn) ||
            !Constants.AUTHENTICATED_PAGES_FOR_NORMAL_USERS.Contains(requestedPath))
        {
            filterContext.Result = new JsonResult(new { HttpStatusCode.Unauthorized });
        }
    }
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
    }
}

HomeController

public class HomeController : Controller
{
    IUserApi _userApi;
    ILoginTokenKeyApi _loginTokenKey;
    IMemoryCache _memoryCache;

    public HomeController(IUserApi userApi, ILoginTokenKeyApi loginTokenKey, IMemoryCache memoryCache)
    {
        _loginTokenKey = loginTokenKey;
        _userApi = userApi;

        _memoryCache = memoryCache;
    }

    [AuthenticationRequired] // There is AN ERROR !!
    public IActionResult Example()
    {
        return View();
    }
}

エラー:

エラーCS7036 'AuthenticationRequiredAttribute.AuthenticationRequiredAttribute(IMemoryCache)' Project.Ground.WebUIの必須仮パラメーター 'memoryCache'に対応する引数が指定されていません

実際の問​​題は、属性クラスで依存性注入を使用できないです。

パラメーターなしでその属性を使用します。それを解決する解決策はありますか?依存性注入を使用しますが、属性には使用できません。どのように使用できますか?

6
canmustu

ドキュメント に従って、ここにいくつかのオプションがあります:

DIからアクセスする必要がある依存関係がフィルターにある場合、いくつかのサポートされるアプローチがあります。次のいずれかを使用して、クラスまたはアクションメソッドにフィルターを適用できます。

ServiceFilterまたはTypeFilter属性

これをすぐに機能させたい場合は、最初の2つのオプションのいずれかを使用して、コントローラーまたはコントローラーアクションにフィルターを適用できます。これを行う場合、フィルターは属性そのものである必要はありません。

[TypeFilter(typeof(ExampleActionFilter))]
public IActionResult Example()
    => View();

ExampleActionFilterは、たとえば IAsyncActionFilter そして、コンストラクター注入を使用して物事に直接依存できます。

public class ExampleActionFilter : IAsyncActionFilter
{
    private readonly IMemoryCache _memoryCache;
    public ExampleActionFilter(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    { … }
}

代わりに[ServiceFilter]属性を使用して同じ効果を得ることができますが、ExampleActionFilterの依存関係注入コンテナーにStartupを登録する必要もあります。

フィルター工場

さらに柔軟性が必要な場合は、独自のフィルターファクトリを実装できます。これにより、ファクトリコードを記述して、実際のフィルターインスタンスを自分で作成できます。上記のExampleActionFilterの可能な実装は次のようになります。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExampleActionFilterAttribute : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return serviceProvider.GetService<ExampleActionFilter>();
    }
}

次に、その[ExampleActionFilter]属性を使用して、DIコンテナーを使用して、MVCフレームワークにExampleActionFilterのインスタンスを作成させます。

この実装は、基本的にServiceFilterAttributeと同じものであることに注意してください。自分で実装すると、ServiceFilterAttributeを直接使用する必要がなくなり、独自の属性を持つことができます。

サービスロケーターの使用

最後に、コンストラクターの挿入を完全に回避できる別のクイックオプションがあります。これは、サービスロケーターパターンを使用して、フィルターが実際に実行されるときにサービスを動的に解決します。そのため、依存関係を注入して直接使用する代わりに、コンテキストから明示的に取得します。

public class ExampleActionFilter : ActionFilterAttribute
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var memoryCache = context.HttpContext.RequestServices.GetService<IMemoryCache>();

        // …
    }
}
15
poke

構築時に解決する代わりに、ActionExecutingContext.HttpContext.RequestServicesは、リクエスト時にリクエストのサービスコンテナへの参照を提供する必要があります。

そう:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    var svc = filterContext.HttpContext.RequestServices;
    var memCache = svc.GetService<IMemoryCache>();
    //..etc
7
spender