在 Autofac 中注册的 .Net Core 工作单元抛出 System.ObjectDisposedException
Unit of work with .Net Core registered in Autofac throws System.ObjectDisposedException
我在一个 HttpRequest 调用的范围内尝试了两次通过工作单元我的存储库,但第二次我遇到了 System.ObjectDisposedException 异常。有人可以帮助我吗?
我的数据库上下文
private readonly string connectionString;
public SwapDealContext(IConfigurationManager configurationManager)
: base()
{
this.connectionString = configurationManager.DbConnectionString;
}
public SwapDealContext(DbContextOptions<SwapDealContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(this.connectionString);
}
}
public virtual DbSet<User> Users { get; set; }
AutoFac 模块:
public class DataAccessAutoFacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
Assembly assembly = typeof(DataAccessAutoFacModule).Assembly;
base.Load(builder);
builder.RegisterAssemblyTypes(assembly).AsSelf().AsImplementedInterfaces().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork.UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWorkFactory>().As<IUnitOfWorkFactory>().InstancePerLifetimeScope();
}
}
接口:
public interface IUnitOfWorkFactory
{
IUnitOfWork CreateUnitOfWork();
}
public interface IUnitOfWork : IDisposable
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
T GetRepository<T>()
where T : class;
}
实现:
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext dbContext;
private readonly Dictionary<string, object> repositories;
private readonly ILifetimeScope lifetimeScope;
public UnitOfWork(
DbContext dbContext,
ILifetimeScope lifetimeScope)
{
this.dbContext = dbContext;
this.lifetimeScope = lifetimeScope;
this.repositories = new Dictionary<string, object>();
}
public void Dispose()
{
this.dbContext.Dispose();
this.repositories.Clear();
}
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
try
{
int changes = await this.dbContext.SaveChangesAsync(cancellationToken);
return changes;
}
catch (Exception ex)
{
throw;
}
}
public T GetRepository<T>()
where T : class
{
var typeName = typeof(T).Name;
if (!this.repositories.ContainsKey(typeName))
{
T instance = this.lifetimeScope.Resolve<T>(new TypedParameter(typeof(DbContext), this.dbContext));
this.repositories.Add(typeName, instance);
}
return (T)this.repositories[typeName];
}
}
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private readonly ILifetimeScope lifetimeScope;
private readonly SwapDealContext context;
public UnitOfWorkFactory(
SwapDealContext context,
ILifetimeScope lifetimeScope)
{
this.context = context;
this.lifetimeScope = lifetimeScope;
}
public IUnitOfWork CreateUnitOfWork()
{
return new UnitOfWork(this.context, this.lifetimeScope);
}
}
服务:
public async Task<IList<UserDetails>> GetAllUsers()
{
using (var uow = this.unitOfWorkFactory.CreateUnitOfWork())
{
var userRepo = uow.GetRepository<IUserRepository>();
var result = await userRepo.GetAllUsers();
return Mapper.Map<List<UserDetails>>(result);
}
}
控制器
public class UserController : ControllerBase
{
private readonly IUserService userService;
private readonly ILogger logger;
public UserController(IUserService userService, ILogger logger)
{
this.userService = userService;
this.logger = logger;
}
[HttpGet]
[Route("users")]
[ProducesResponseType(typeof(IList<UserDetails>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[AllowAnonymous]
public async Task<IActionResult> GetUsersAnync()
{
try
{
var users = await userService.GetAllUsers();
var users2 = await userService.GetAllUsers();
if (!users.Any())
{
return NotFound($"No one user were found");
}
return Ok(users);
}
catch (Exception ex)
{
logger.ErrorFormat("Could not get users due to: {0}", ex, ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
}
}
}
堆栈跟踪:
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Internal.IDbContextDependencies.get_QueryProvider()
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ParameterExtractingExpressionVisitor..ctor(IEvaluatableExpressionFilter evaluatableExpressionFilter, IParameterValues parameterValues, IDiagnosticsLogger`1 logger, DbContext context, Boolean parameterize, Boolean generateContextAccessors)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryModelGenerator.ExtractParameters(IDiagnosticsLogger`1 logger, Expression query, IParameterValues parameterValues, Boolean parameterize, Boolean generateContextAccessors)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.IncludableQueryable`2.System.Collections.Generic.IAsyncEnumerable<TEntity>.GetEnumerator()
at System.Linq.AsyncEnumerable.Aggregate_[TSource,TAccumulate,TResult](IAsyncEnumerable`1 source, TAccumulate seed, Func`3 accumulator, Func`2 resultSelector, CancellationToken cancellationToken)
at SwapDeal.DataAccess.Repositories.UserRepository.GetAllUsers() in C:\Users\osmachenko\personal\VAO\SwapDeal.DataAccess\Repositories\UserRepository.cs:line 23
at SwapDeal.BizLogic.Services.UserService.GetAllUsers() in C:\Users\osmachenko\personal\VAO\SwapDeal.BizLogic\Services\UserService.cs:line 40
at SwapDeal.WebApi.Controllers.UserController.GetUsersAnync() in C:\Users\osmachenko\personal\VAO\SwapDeal.WebApi\Controllers\UserController.cs:line 37
所以,当我在 Controller 中调用 GetAllUsers 方法两次时,我得到了 System.ObjectDisposedException。
您看到上下文已被处置的原因是因为您正在处置它。
如果我们追踪...
- 您在 Autofac 注册的每种类型似乎都已注册
InstancePerLifetimeScope
。这意味着您将在一生中获得一个范围。在 ASP.NET Core 中,这相当于每个请求一个实例(基本上)。
- 控制器将在其控制器中获得一个
IUserService
。我们看不到整个 IUserService
是什么样子,但我们确实在示例代码中看到了 GetAllUsers
。
GetAllUsers
将操作包装在 using
语句中 - 它创建, 然后在完成后处理 工作单元。
- 在
UnitOfWork
的构造函数中,你传入 DbContext
- 一个实例 你得到那个请求,然后...
- 在
UnitOfWork.Dispose
中,您处理了 DbContext
。由于那个 using
语句,这发生在 GetAllUsers
的末尾。
public async Task<IList<UserDetails>> GetAllUsers()
{
// The factory creates the unit of work here...
using (var uow = this.unitOfWorkFactory.CreateUnitOfWork())
{
var userRepo = uow.GetRepository<IUserRepository>();
var result = await userRepo.GetAllUsers();
return Mapper.Map<List<UserDetails>>(result);
// At the end of this using statement, the unit of work gets disposed
// and in UnitOfWork.Dispose() you dispose of the DbContext.
}
}
如果要调用操作两次,您需要:
- 不在工作单元结束时处理上下文; 或
- 为每个工作单元提供自己的数据库上下文。
我在一个 HttpRequest 调用的范围内尝试了两次通过工作单元我的存储库,但第二次我遇到了 System.ObjectDisposedException 异常。有人可以帮助我吗?
我的数据库上下文
private readonly string connectionString;
public SwapDealContext(IConfigurationManager configurationManager)
: base()
{
this.connectionString = configurationManager.DbConnectionString;
}
public SwapDealContext(DbContextOptions<SwapDealContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(this.connectionString);
}
}
public virtual DbSet<User> Users { get; set; }
AutoFac 模块:
public class DataAccessAutoFacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
Assembly assembly = typeof(DataAccessAutoFacModule).Assembly;
base.Load(builder);
builder.RegisterAssemblyTypes(assembly).AsSelf().AsImplementedInterfaces().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork.UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWorkFactory>().As<IUnitOfWorkFactory>().InstancePerLifetimeScope();
}
}
接口:
public interface IUnitOfWorkFactory
{
IUnitOfWork CreateUnitOfWork();
}
public interface IUnitOfWork : IDisposable
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
T GetRepository<T>()
where T : class;
}
实现:
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext dbContext;
private readonly Dictionary<string, object> repositories;
private readonly ILifetimeScope lifetimeScope;
public UnitOfWork(
DbContext dbContext,
ILifetimeScope lifetimeScope)
{
this.dbContext = dbContext;
this.lifetimeScope = lifetimeScope;
this.repositories = new Dictionary<string, object>();
}
public void Dispose()
{
this.dbContext.Dispose();
this.repositories.Clear();
}
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
try
{
int changes = await this.dbContext.SaveChangesAsync(cancellationToken);
return changes;
}
catch (Exception ex)
{
throw;
}
}
public T GetRepository<T>()
where T : class
{
var typeName = typeof(T).Name;
if (!this.repositories.ContainsKey(typeName))
{
T instance = this.lifetimeScope.Resolve<T>(new TypedParameter(typeof(DbContext), this.dbContext));
this.repositories.Add(typeName, instance);
}
return (T)this.repositories[typeName];
}
}
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private readonly ILifetimeScope lifetimeScope;
private readonly SwapDealContext context;
public UnitOfWorkFactory(
SwapDealContext context,
ILifetimeScope lifetimeScope)
{
this.context = context;
this.lifetimeScope = lifetimeScope;
}
public IUnitOfWork CreateUnitOfWork()
{
return new UnitOfWork(this.context, this.lifetimeScope);
}
}
服务:
public async Task<IList<UserDetails>> GetAllUsers()
{
using (var uow = this.unitOfWorkFactory.CreateUnitOfWork())
{
var userRepo = uow.GetRepository<IUserRepository>();
var result = await userRepo.GetAllUsers();
return Mapper.Map<List<UserDetails>>(result);
}
}
控制器
public class UserController : ControllerBase
{
private readonly IUserService userService;
private readonly ILogger logger;
public UserController(IUserService userService, ILogger logger)
{
this.userService = userService;
this.logger = logger;
}
[HttpGet]
[Route("users")]
[ProducesResponseType(typeof(IList<UserDetails>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[AllowAnonymous]
public async Task<IActionResult> GetUsersAnync()
{
try
{
var users = await userService.GetAllUsers();
var users2 = await userService.GetAllUsers();
if (!users.Any())
{
return NotFound($"No one user were found");
}
return Ok(users);
}
catch (Exception ex)
{
logger.ErrorFormat("Could not get users due to: {0}", ex, ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
}
}
}
堆栈跟踪:
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Internal.IDbContextDependencies.get_QueryProvider()
at Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal.ParameterExtractingExpressionVisitor..ctor(IEvaluatableExpressionFilter evaluatableExpressionFilter, IParameterValues parameterValues, IDiagnosticsLogger`1 logger, DbContext context, Boolean parameterize, Boolean generateContextAccessors)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryModelGenerator.ExtractParameters(IDiagnosticsLogger`1 logger, Expression query, IParameterValues parameterValues, Boolean parameterize, Boolean generateContextAccessors)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.System.Collections.Generic.IAsyncEnumerable<TResult>.GetEnumerator()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.IncludableQueryable`2.System.Collections.Generic.IAsyncEnumerable<TEntity>.GetEnumerator()
at System.Linq.AsyncEnumerable.Aggregate_[TSource,TAccumulate,TResult](IAsyncEnumerable`1 source, TAccumulate seed, Func`3 accumulator, Func`2 resultSelector, CancellationToken cancellationToken)
at SwapDeal.DataAccess.Repositories.UserRepository.GetAllUsers() in C:\Users\osmachenko\personal\VAO\SwapDeal.DataAccess\Repositories\UserRepository.cs:line 23
at SwapDeal.BizLogic.Services.UserService.GetAllUsers() in C:\Users\osmachenko\personal\VAO\SwapDeal.BizLogic\Services\UserService.cs:line 40
at SwapDeal.WebApi.Controllers.UserController.GetUsersAnync() in C:\Users\osmachenko\personal\VAO\SwapDeal.WebApi\Controllers\UserController.cs:line 37
所以,当我在 Controller 中调用 GetAllUsers 方法两次时,我得到了 System.ObjectDisposedException。
您看到上下文已被处置的原因是因为您正在处置它。
如果我们追踪...
- 您在 Autofac 注册的每种类型似乎都已注册
InstancePerLifetimeScope
。这意味着您将在一生中获得一个范围。在 ASP.NET Core 中,这相当于每个请求一个实例(基本上)。 - 控制器将在其控制器中获得一个
IUserService
。我们看不到整个IUserService
是什么样子,但我们确实在示例代码中看到了GetAllUsers
。 GetAllUsers
将操作包装在using
语句中 - 它创建, 然后在完成后处理 工作单元。- 在
UnitOfWork
的构造函数中,你传入DbContext
- 一个实例 你得到那个请求,然后... - 在
UnitOfWork.Dispose
中,您处理了DbContext
。由于那个using
语句,这发生在GetAllUsers
的末尾。
public async Task<IList<UserDetails>> GetAllUsers()
{
// The factory creates the unit of work here...
using (var uow = this.unitOfWorkFactory.CreateUnitOfWork())
{
var userRepo = uow.GetRepository<IUserRepository>();
var result = await userRepo.GetAllUsers();
return Mapper.Map<List<UserDetails>>(result);
// At the end of this using statement, the unit of work gets disposed
// and in UnitOfWork.Dispose() you dispose of the DbContext.
}
}
如果要调用操作两次,您需要:
- 不在工作单元结束时处理上下文; 或
- 为每个工作单元提供自己的数据库上下文。