无法访问从ActionFilterAttribute派生的类中的存储库(或DbContext)

时间:2019-10-05 02:19:05

标签: c# asp.net-core asp.net-core-2.2

我有一个使用存储库模式的ASP.NET Core 2.2 MVC Web应用程序。我创建了一个名为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
        });
    }
}

所以我的问题是如何从我的LogAttribute类访问我的存储库(或至少我的DbContext)?

2 个答案:

答案 0 :(得分:0)

你不知道。属性不能具有构造函数注入的参数,并且它们的生命周期是无限的,这使它们成为与ASP.NET Core管道集成的非常糟糕的选择。

因此,属性本身不应执行任何“繁重的任务”。我觉得各种在线指南显示了Debug.WriteLine中琐碎的日志记录(使用ActionFilterAttribute)正在给读者带来伤害(例如(请勿这样做!!){ {3}})

如果您使用的是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.tutorialsteacher.com/mvc/action-filters-in-mvc

答案 1 :(得分:0)

您可以100%注入动作过滤器。问题是当它用作属性时,因为属性是内联实例化的,因此没有任何机会让服务集合实际进行注入。但是,使用TypeFilterAttribute可以解决此问题:

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

然后,您将拥有一个类似的构造函数:

public LogAttribute(string description, IMyRepository repository)