DbContext 在使用异步任务方法时被释放
Posted
技术标签:
【中文标题】DbContext 在使用异步任务方法时被释放【英文标题】:DbContext Gets Disposed Upon Using async Task method 【发布时间】:2019-01-08 13:58:57 【问题描述】:我目前正在使用异步任务方法来实现 IAuthenticationFilter 接口。成功登录后,我将尝试访问具有此属性的 API,它会正常工作。但是,一旦我返回并再次访问 API,就会抛出异常。
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
var token = context.Request.Headers.Authorization.Parameter;
var principal = await AuthenticateToken(token)
// Other code here ...
protected Task<IPrincipal> AuthenticateToken(string token)
var secretKey = _authenticationBusiness.GetSecretKey(); // error triggers here.
if (principal == null)
context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
else
context.Principal = principal;
//AuthenticationBusiness.cs
public string GetSecretKey()
using (_unitOfWork)
var token = _unitOfWork.Tokens.GetToken();
return token.SecretKey ?? string.Empty;
//Dependency Injection using Unity
container.RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager());
container.RegisterType<IContext, Context>(new HierarchicalLifetimeManager());
//UnitOfWork.cs
private readonly IContext _context;
public UnitOfWork(IContext context, IJWTRepository tokens)
_context = context;
Tokens = tokens;
public IJWTRepository Tokens get; private set;
public void Dispose()
_context.Dispose();
//Context.cs
public class Context : DbContext, IContext
public new void SaveChanges()
base.SaveChanges();
public new void Dispose()
base.Dispose();
//JWTRepository.cs
public class JWTRepository : Repository<JsonWebToken>, IJWTRepository
public JWTRepository(Context context) : base(context)
public JsonWebToken GetToken()
return Context.Tokens
.OrderBy(jwt => jwt.Id)
.Take(1)
.SingleOrDefault();
private Context Context => _context as Context;
如果我尝试删除此属性并多次访问 API 并没有发生任何错误,所以我假设这与该属性具有异步方法这一事实有关?
【问题讨论】:
因为你正在处理它。您的GetSecretKey()
被包裹在 using()
中,这将处理 _unitOfWork
using() 本质上将在项目超出范围后处理该项目。如果您正在执行其他任何异步操作(我假设您返回任务等),您可能还希望将您的 CancellationToken 传递给您的 AuthenticateToken() 方法。
@JohanP:如果处理是问题,当我删除这个属性时它怎么能正常工作?这意味着如果我尝试在没有 IAuthenticationFilter 的情况下多次访问 API 一切正常。
@GlennWatson:AuthenticateToken 方法中没有异步方法。我更新了方法。
当你多次访问它时,你会得到一个新的上下文,你的 DI 会为每个请求注入一个新的,当你有过滤器时,上下文仍然是请求中的那个,你在一个请求中多次访问 unitOfWork。
【参考方案1】:
当 IDisposable 对象的生命周期被限制为单个 方法,你应该在 using 语句中声明和实例化它。 using 语句调用对象中的 Dispose 方法 正确的方法,并且(当您如前所示使用它时)它也会导致 一旦调用 Dispose,对象本身就会超出范围。之内 using 块,该对象是只读的,不能修改或 重新分配。
using Statement (C# Reference)
在您的代码中,问题在于您将 GetSecretkey()
包装到 using()
中,这将处理 _unitOfWork
并且当您再次尝试访问它时会显示错误。
希望此代码对您有用。
//AuthenticationBusiness.cs
public string GetSecretKey()
var token = _unitOfWork.Tokens.GetToken();
return token.SecretKey ?? string.Empty;
【讨论】:
它就像一个魅力。但是,如果抛出异常,我将如何正确处理 _unitOfWork,比如_unitOfWork.Tokens.GetToken();
?
@JoshMonreal 你的 DI 会为你处理它,经验法则是创建对象的东西负责处理它。【参考方案2】:
你的函数AuthenticateToken
中的问题。使用 async-await 时,如果 Task Dispose 项目,请确保在返回之前等待每个 Task。见what is the purpose of return await。第一个答案集中在一次性物品上
我假设您省略了方法AuthenticateToken
的部分内容,因为我没有说返回值。
解决方法:声明方法async,等待Task返回
async Task<IPrincipal> AuthenticateToken(string token)
var secretKey = _authenticationBusiness.GetSecretKey();
...
// Somewhere there is a Task involved,
Task<IPrincipal> myTask = ...
// instead of return myTask:
return await myTask;
【讨论】:
以上是关于DbContext 在使用异步任务方法时被释放的主要内容,如果未能解决你的问题,请参考以下文章