无法从派生自 ActionFilterAttribute 的 class 访问存储库(或 DbContext)
Unable to access repository (or DbContext) from a class that is derived from ActionFilterAttribute
我有一个使用存储库模式的 ASP.NET Core 2.2 MVC Web 应用程序。我创建了一个名为 LogAttribute
的 class 派生自 ActionFilterAttribute 以便我可以在执行控制器操作后记录信息。
这是在 mvc 控制器中使用此操作过滤器属性的示例 class:
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
时,它应该记录 "Go to Home Page"。当我转到 /About
页面时,它应该记录 "Go to About Page".
但是,我不知道如何从 LogAttribute
class 访问我的存储库。这是 LogAttribute
class:
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
class 访问我的存储库(或至少我的 DbContext)?
你不知道。属性不能有构造函数注入参数,并且它们的生命周期是无限的,这使得它们成为与 ASP.NET 核心管道集成的非常糟糕的选择。
因此,属性本身不应执行任何 "heavy lifting"。我觉得在 ActionFilterAttribute
中显示琐碎日志记录(使用 Debug.WriteLine
)的各种在线教程和指南正在损害他们的读者(例如(不要这样做!) https://www.tutorialsteacher.com/mvc/action-filters-in-mvc )
如果您使用的是 ASP.NET 核心,则分别实现 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 )
您可以 100% 注入动作过滤器。问题是当它用作属性时,因为属性是内联实例化的,没有为服务集合提供实际执行注入的机会。但是,有一个解决方法 TypeFilterAttribute
:
[TypeFilter(typeof(LogAttribute),
Arguments = new object[] { "Go to Home Page" })]
然后,您将拥有如下构造函数:
public LogAttribute(string description, IMyRepository repository)
我有一个使用存储库模式的 ASP.NET Core 2.2 MVC Web 应用程序。我创建了一个名为 LogAttribute
的 class 派生自 ActionFilterAttribute 以便我可以在执行控制器操作后记录信息。
这是在 mvc 控制器中使用此操作过滤器属性的示例 class:
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
时,它应该记录 "Go to Home Page"。当我转到 /About
页面时,它应该记录 "Go to About Page".
但是,我不知道如何从 LogAttribute
class 访问我的存储库。这是 LogAttribute
class:
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
class 访问我的存储库(或至少我的 DbContext)?
你不知道。属性不能有构造函数注入参数,并且它们的生命周期是无限的,这使得它们成为与 ASP.NET 核心管道集成的非常糟糕的选择。
因此,属性本身不应执行任何 "heavy lifting"。我觉得在 ActionFilterAttribute
中显示琐碎日志记录(使用 Debug.WriteLine
)的各种在线教程和指南正在损害他们的读者(例如(不要这样做!) https://www.tutorialsteacher.com/mvc/action-filters-in-mvc )
如果您使用的是 ASP.NET 核心,则分别实现 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 )
您可以 100% 注入动作过滤器。问题是当它用作属性时,因为属性是内联实例化的,没有为服务集合提供实际执行注入的机会。但是,有一个解决方法 TypeFilterAttribute
:
[TypeFilter(typeof(LogAttribute),
Arguments = new object[] { "Go to Home Page" })]
然后,您将拥有如下构造函数:
public LogAttribute(string description, IMyRepository repository)