我正在使用API,并且在对服务进行多次调用时遇到问题,而且方法不同,我让每个方法都创建并使用了新的DBContext(或者至少是这样做的目的),但是在第一个服务调用之后其他人则抱怨说DBContext已经被处理掉了,我希望您能为我指明正确的方向,因为据我所知,我正在为每个调用创建一个新的上下文-显然我在这里做错了,没有任何帮助将不胜感激。
我得到的实际错误是“无法访问已处置的对象。”
我知道我可以将db交互和上下文创建代码从服务中拉出,并放到此处的controller方法中(这是一个简化的示例),但是将需要在应用程序的其他部分中使用更多的服务,并且遇到了也存在问题,因此想尝试找出导致此问题的原因,以便可以将修复程序应用于其他地方。
这是其中涉及的简化类。
public class UserController : Controller
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
this.userService = userService;
}
[HttpPost]
[ActionName("PostUserDetails")]
public async Task<IActionResult> PostUserDetails([FromBody]UserDetailsContract userDetailsContract)
{
// this call is fine
var user = await userService.GetUserByCode(userDetailsContract.Code);
if (user == null)
{
return BadRequest("User not found");
}
// this call fails with the object disposed error
var userDetails = await userService.GetUserDetailsByCode(userDetailsContract.Code);
if (userDetails != null)
{
return BadRequest("UserDetails already exists");
}
// .. go on to save new entity
return Ok();
}
}
public class UserService : IUserService
{
private readonly IDatabaseFactory databaseFactory;
public UserService(IDatabaseFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
public async Task<User> GetUserByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.Users.GetByCode(code);
}
}
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
}
public class ApiDbContext : DbContext, IApiDbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserDetail> UserDetails { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=192.168.1.1;Database=dbname;User Id=user; Password=pwd; MultipleActiveResultSets=True;");
}
}
public class DatabaseFactory : IDatabaseFactory
{
public IApiDatabase Create()
{
return new ApiDatabase(new ApiDbContext());
}
}
public class ApiDatabase : RepositoriesBase, IApiDatabase
{
private IUserRepository userRepository;
private IUserDetailsRepository userDetailsRepository;
public ApiDatabase(ApiDbContext context) : base(context)
{
}
public IUserRepository Users => userRepository ?? (userRepository = new UserRepository(context));
public IUserDetailsRepository UserExchange => userDetailsRepository ?? (userDetailsRepository = new UserDetailsRepository(context));
}
public abstract class RepositoriesBase : IRepositories
{
internal readonly ApiDbContext context;
private bool isDisposing;
protected RepositoriesBase(ApiDbContext context)
{
}
public void Dispose()
{
if (!isDisposing)
{
isDisposing = true;
context?.Dispose();
}
}
public Task SaveChanges() => context.SaveChangesAsync();
}
public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(ApiDbContext context) : base(context)
{
}
public async Task<User> GetByCode(string code)
{
return Filter(x => x.code == code).Result.FirstOrDefault();
}
}
public class UserDetailsRepository : Repository<UserDetail>, IUserDetailRepository
{
public UserExchangeRepository(ApiDbContext context) : base(context)
{
}
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
private readonly ApiDbContext context;
public Repository(ApiDbContext context) => this.context = context;
public async Task Add(T entity)
{
context.Set<T>().Add(entity);
}
public async Task Add(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Add(entity);
}
}
public async Task Delete(T entity)
{
context.Set<T>().Remove(entity);
}
public async Task Delete(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Remove(entity);
}
}
public async Task Delete(int id)
{
var entityToDelete = context.Set<T>().FirstOrDefault(e => e.Id == id);
if (entityToDelete != null)
{
context.Set<T>().Remove(entityToDelete);
}
}
public async Task Update(T entity)
{
context.Set<T>().Update(entity);
}
public async Task Edit(T entity)
{
var editedEntity = context.Set<T>().FirstOrDefault(e => e.Id == entity.Id);
editedEntity = entity;
}
public async Task<IEnumerable<T>> GetAll(Expression<Func<T, bool>> predicate = null)
{
var query = context.Set<T>().Include(context.GetIncludePaths(typeof(T)));
if (predicate != null)
{
query = query.Where(predicate);
}
return await query.ToListAsync();
}
public async Task<T> GetById(int id)
{
return context.Set<T>().FirstOrDefault(e => e.Id == id);
}
public async Task<IEnumerable<T>> Filter()
{
return context.Set<T>();
}
public virtual async Task<IEnumerable<T>> Filter(Func<T, bool> predicate)
{
return context.Set<T>().Where(predicate);
}
public async Task SaveChanges() => context.SaveChanges();
}
在我的DI配置中,我将DatabaseFactory和UserService定义为单例。
错误:“无法访问已处置的对象。”
更多错误详细信息:“位于 Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()在 Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
在Microsoft.EntityFrameworkCore.DbContext.get_Model()在 Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet
1.get_EntityQueryable() 在 Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator() at System.Linq.Enumerable.WhereEnumerableIterator
1.MoveNext()在 System.Linq.Enumerable.Any [TSource](IEnumerable1 source, Func
2 谓词) App.Api.Controllers.UserController.PostUserDetail(UserDetailContract userDetailContract)中 D:\ Repositories \ application \ src \ App \ Api \ Controllers \ UserController.cs:line 89英寸
谢谢
答案 0 :(得分:0)
我认为您可能是延迟执行的受害者。下面的代码创建了ApiDatabase
的实例,该实例又创建了一个新的ApiDbContext
:
public IApiDatabase Create() //in DatabaseFactory
{
return new ApiDatabase(new ApiDbContext());
}
顺便说一句,我在这里检测到代码气味,因为ApiDbContext
是一次性的,所以您应该跟踪此引用并将其正确处置。
无论如何,ApiDatabase
是一次性的,因为它被包裹在using
语句中,所以我认为上下文是在调用GetByUserId
之后处理的:
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
注意,您将返回一个枚举。我认为您使用它的时间可能尚未实现,因此出现了错误。将强制转换添加到数组以强制实现:
return await Filter(x => x.UserId == userId).ToArray();
答案 1 :(得分:0)
您的问题是此方法的签名:
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
IEnumerable<T>
是一个可枚举的对象,通常是惰性计算的。同时,一旦定义了,Task<T>
就被认为是完整的(而不是完成时)。一旦定义了,便处理了上下文。如果代码是同步的,您将遇到同样的问题。
解决方法是在处理上下文之前“确定”(评估)可枚举的值:
public async Task<IReadOnlyCollection<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code).ToList();
}
}