【问题标题】:Unable to access repository (or DbContext) from a class that is derived from ActionFilterAttribute无法从派生自 ActionFilterAttribute 的类访问存储库(或 DbContext)
【发布时间】:2020-02-03 06:59:03
【问题描述】:

我有一个使用存储库模式的 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)?

【问题讨论】:

  • @JohnWu - 您提供的链接建议使用可以接收依赖项的公共设置器创建一个属性(即public ApplicationDbContext Context { get; set; })。但是,我已经尝试过了,但它仍然给我NullReferenceException: Object reference not set to an instance of an object 错误,因为 Context 为空。
  • @Dai - 我正在使用 ASP.NET Core 2.2 (Microsoft.AspNetCore.*)。我已经在我的问题中提到了这一点。
  • @JohnWu 链接到的 QA 是针对非核心 ASP.NET MVC - 虽然令人困惑的是,一些已发布的答案确实适用于 ASP.NET Core。
  • 是的,该链接建议适用于 ASP.NET 4.x MVC 应用程序,但不适用于 ASP.NET Core。

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


【解决方案1】:

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

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

然后,你会有一个像这样的构造函数:

public LogAttribute(string description, IMyRepository repository)

【讨论】:

  • 根据link。我宁愿选择实现 IFilterFactory 并使用我的自定义 LogFilterFactory 而不是 TypeFilter(它是 IFilterFactory 的实现)。所以现在,我的属性看起来像[LogFilterFactory(Description = "Go to Home Page")]
  • ServiceFilterTypeFilter 和实现自定义 IFilterFactory 似乎是在属性中注入依赖项的推荐方法。使用这些方法而不是RequestServices.GetService 服务定位器(反)模式。
【解决方案2】:

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

因此,属性本身不应执行任何“繁重的工作”。我觉得在ActionFilterAttribute 中显示琐碎日志记录(使用Debug.WriteLine)的各种在线教程和指南正在伤害他们的读者(例如(不要这样做!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)

【讨论】:

  • 您提供的链接非常有帮助!它不仅提供了在动作过滤器中注入依赖项的解决方案,而且还讨论了服务定位器反模式,并建议使用自定义过滤器工厂作为更好的选择。
猜你喜欢
  • 2017-11-28
  • 1970-01-01
  • 2023-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-22
  • 2015-12-30
  • 2018-02-04
相关资源
最近更新 更多