【发布时间】:2021-03-28 22:34:33
【问题描述】:
我想创建一个全局操作过滤器来审核对我的 API 的所有请求。我想使用全局注册的ActionFilter 来确保所有 API 控制器操作都经过审核。我想注入AuditService 的作用域实例,但是在调用_auditService.Create 时得到System.ObjectDisposedException。将作用域服务注入ActionFilter 以使其在调用OnActionExecuted 之前不会被处理的正确方法是什么?该服务也在OnActionExecuting 事件之前被销毁。
启动代码:
public void ConfigureServices(IServiceCollection services)
{
// ..
services.AddControllers(c => c.Filters.Add<AuditFilter>());
services.AddDbContext<MyDbContext>(c => c.UseSqlServer("ConnectionString"));
services.AddScoped<IAuditService, AuditService>();
// ..
}
动作过滤器:
public class AuditFilter : IActionFilter
{
private readonly IAuditService _auditService;
public AuditFilter(IAuditService auditService)
{
_auditService = auditService;
}
public void OnActionExecuting(ActionExecutingContext context)
{
}
public async void OnActionExecuted(ActionExecutedContext context)
{
string username = ClaimsPrincipal.Current?.Identity?.Name;
string remoteAddr = $"{context.HttpContext.Connection.RemoteIpAddress}:{context.HttpContext.Connection.RemotePort}";
string queryString = context.HttpContext.Request.QueryString.HasValue ? context.HttpContext.Request.QueryString.Value : null;
using StreamReader reader = new StreamReader(context.HttpContext.Request.Body);
string body = await reader.ReadToEndAsync();
body = body.Length > 0 ? body : null;
// System.ObjectDisposedException: 'Cannot access a disposed context instance
await _auditService.Create(username, remoteAddr, context.HttpContext.Request.Method,
context.HttpContext.Request.Path, queryString, body, DateTime.Now);
}
}
审计服务:
public class AuditService : IAuditService
{
private DRSContext Context { get; }
public AuditService(DRSContext context)
{
Context = context;
}
public async Task<bool> Create(string username, string remoteAddr, string httpMethod, string path, string query,
string body, DateTime timestamp)
{
await Context.AuditLogs.AddAsync(new AuditLog
{
Username = username,
RemoteAddress = remoteAddr,
HttpMethod = httpMethod,
Path = path,
Query = query,
Body = body,
Timestamp = timestamp
});
return await Context.SaveChangesAsync() > 0;
}
// ..
}
【问题讨论】:
-
注入的
IAuditService实现很可能在请求后被释放时使用了作用域DbContext。使用HttpContext.RequestServices创建本地范围并执行您的功能。还有async void被使用。 -
首先您应该使用
IResourceFilter来确保所有资源访问都被审计(页面处理程序将不调用IActionFilter)。其次,我认为这可能无法显示全貌,问题可能出在其他地方,或者您的IAuditService的实现内部有问题。一般来说,这可能是由错误的缓存、错误的后台任务运行方式、显式处理 DbContext 造成的……您需要扫描所有项目才能找到它。 -
@KingKing 我已将我的
AuditService课程添加到问题中。我会调查IResourceFilter。 -
您的
Dispose实现可能是这里的问题,不知何故,在再次使用DbContext之前调用了它。您不需要像那样显式处置DbContext。因为它将由 DI 容器管理。而Dispose通常用于非托管资源。 -
好的,我已经在我的回答中总结了这里评论的所有想法(正如@Nkosi 所指出的那样)。
标签: c# asp.net-core asp.net-web-api dependency-injection