Перейти к содержанию

Использование внедрения зависимостей с атрибутами фильтра

Атрибуты C# не позволяют передавать зависимости в свои конструкторы (кроме постоянных значений), что усложняет передачу зависимостей в фильтры, реализуемые как атрибуты.
Одним из способов обхода этого ограничения является использование локатора сервисов (Service Locator), а именно:

var service = (RecipeService) context.HttpContext.RequestServices.GetService(typeof(RecipeService));

Однако, в мире внедрения зависимостей Service Locator считается антипаттерном.

Есть другой способ. Ключевой момент здесь — разделение фильтра на две части, а именно — класс фильтра, содержащий функциональность и атрибут, сообщающий фреймворку где и когда использовать фильтр.

public class EnsureRecipeExistsFilter : IActionFilter //Не наследует Attribute
{
    private readonly RecipeService _service;
    public EnsureRecipeExistsFilter(RecipeService service)
    {
        _service = service;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var recipeId = (int) context.ActionArguments["id"];
        if (!_service.DoesRecipeExist(recipeId))
        {
            context.Result = new NotFoundResult();
        }
    }

    public void OnActionExecuted(ActionExecutedContext context) { }
}

public class EnsureRecipeExistsAttribute : TypeFilterAttribute
{
    public EnsureRecipeExistsAttribute:base(typeof(EnsureRecipeExistsFilter)) {}
}

TypeFilterAttribute действует как фабрика для фильтра EnsureRecipeExistsFilter путём реализации интерфейса IFilterFactory. Когда вызывается действие, декорированное атрибутом [EnsureRecipeExists], фреймворк вызывает фабричный метод CreateInstance() для атрибута, создавая новый экземпляр EnsureRecipeExistsFilter, используя контейнер внедрения зависимостей.

По умолчанию такую функциональность предоставляют два аналогичных класса с немного разным поведением:

  • TypeFilterAttribute — загружает все зависимости фильтра из контейнера внедрения зависимостей и использует их для создания нового экземпляра фильтра;
  • ServiceFilterAttribute — загружает сам фильтр из контейнера. При этом необходимо явно зарегистрировать фильтр в контейнере в ConfigureServices.

Последнее обновление : 3 мая 2023 г.
Дата создания : 3 октября 2022 г.

Комментарии

Комментарии