Невозможно получить доступ к хранилищу (или DbContext) из класса, производного от ActionFilterAttribute - PullRequest
1 голос
/ 05 октября 2019

У меня есть веб-приложение ASP.NET Core 2.2 MVC, использующее шаблон репозитория. Я создал класс с именем LogAttribute, который получен из ActionFilterAttribute , чтобы я мог регистрировать информацию после выполнения действий контроллера.

Вот пример использования этого атрибута фильтра действия в mvcкласс контроллера:

public class HomeController : Controller
{
    private readonly IMyRepository _repository;

    public HomeController(IMyRepository repository)
    {
        _repository = repository;
    }

    [Log("Go to Home Page")]
    public async Task<IActionResult> Index()
    {
        ...
    }

    [Log("Go to About Page")]
    public async Task<IActionResult> About()
    {
        ...
    }
}

Поэтому, когда я перехожу на /Home, он должен войти в «Перейти на домашнюю страницу». И когда я перехожу на страницу /About, она должна вести журнал «Перейти к странице».

Однако я не знаю, как получить доступ к своему хранилищу из класса LogAttribute. Вот класс LogAttribute:

public class LogAttribute : ActionFilterAttribute
{
    private IDictionary<string, object> _arguments;
    private IMyRepository _repository;

    public string Description { get; set; }

    public LogAttribute(string description)
    {
        Description = description;
    }

    // // Injecting repository as a dependency in the ctor DOESN'T WORK
    // public LogAttribute(string description, IMyRepository repository)
    // {
    //     Description = description;
    //     _repository = repository;
    // }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        _arguments = filterContext.ActionArguments;
        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var description = Description;

        // NullReferenceException since I don't know
        // how to access _repository from this class
        _repository.AddLog(new LogAction
        {
            Description = description
        });
    }
}

Итак, мой вопрос, как я могу получить доступ к своему хранилищу (или, по крайней мере, к моему DbContext) из моего LogAttribute класса?

Ответы [ 2 ]

0 голосов
/ 05 октября 2019

Вы можете 100% внедрить в фильтр действий. Проблема заключается в том, что когда он используется в качестве атрибута, атрибуты инстанцируются встраиваемыми, что не дает возможности для сбора службы фактически выполнить внедрение. Однако, есть обходной путь для этого с TypeFilterAttribute:

[TypeFilter(typeof(LogAttribute),
    Arguments = new object[] { "Go to Home Page" })]

Тогда у вас будет такой конструктор, как:

public LogAttribute(string description, IMyRepository repository)
0 голосов
/ 05 октября 2019

Ты не. Атрибуты не могут иметь внедренные параметры конструктора, и их время жизни не ограничено, что делает их очень плохим выбором для интеграции с конвейером ASP.NET Core.

Поэтому атрибуты сами по себе не должны выполнять какую-либо «тяжелую работу». Я чувствую, что различные учебные пособия и руководства онлайн, которые показывают тривиальные журналы (используя Debug.WriteLine) внутри ActionFilterAttribute, наносят вред своим читателям (например, ( НЕ ДЕЛАЙТЕ ЭТОГО! ) https://www.tutorialsteacher.com/mvc/action-filters-in-mvc)

Если вы используете ASP.NET Core, то реализуйте IActionFilter (или IAsyncActionFilter) и IFilterFactory отдельно и сделайте IFilterFactory Attribute (вместо IActionFilter)), вот так:

// This class is the attribute. Note that it is not an action filter itself.
// This class cannot have DI constructor injection, but it can access the IServiceProvider.
public class LogAttribute : Attribute, IFilterFactory
{
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return serviceProvider.GetService<LogFilter>();
    }
}


// This class is the actual filter. It is not an attribute.
// This class *can* have DI constructor injection.
public class LogFilter : IActionFilter // or IAsyncActionFilter
{
    public LogFilter( DbContext db )
    {

    }
}

Полный пример можно найти здесь: (https://www.devtrends.co.uk/blog/dependency-injection-in-action-filters-in-asp.net-core)

...