EF6 - Update()失败,多个SaveChangeAsync,但没有多个SaveChanges()
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF6 - Update()失败,多个SaveChangeAsync,但没有多个SaveChanges()相关的知识,希望对你有一定的参考价值。
我目前面临的情况我不太明白。对于每个存储库,我有一个基础,这是它的一部分:
public abstract class BaseRepository<T> : IBaseRepository<T> where T : class
{
/// <summary>
/// Context for the database
/// </summary>
protected readonly DataBaseContext DbContext;
protected readonly DbSet<T> DbSet;
// ...
public virtual T GetById(Guid id)
{
Requires.ArgumentNotNullAndNotDefault(id);
return DbSet.Find(id);
}
public virtual Task<T> GetByIdAsync(Guid id)
{
Requires.ArgumentNotNullAndNotDefault(id);
return DbSet.FindAsync(id);
}
// ...
public virtual void Update(T entity)
{
DbSet.Attach(entity);
DbContext.Entry(entity).State = EntityState.Modified;
}
// ...
public void SaveChanges() => DbContext.SaveChanges();
public Task<int> SaveChangesAsync() => DbContext.SaveChangesAsync();
}
奇怪的是,如果我得到2次实体(CarEntity),让我们说,在同一个背景下,我GetById()
两次并更新2个不同的值,并在每个Update()
,我SaveChanges()
,它抛出以下异常,但仅适用于异步逻辑:
附加“Project.Model.CarEntity”类型的实体失败,因为同一类型的另一个实体已具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。
有我的控制器:
[Route("set_car_color_and_licence_plate_color")]
[ResponseType(typeof(Car))]
[HttpPost]
public Task<IHttpActionResult> SetCarColorAndLicencePlate([FromBody] SetCarColorAndLicencePlateRequest setCarColorAndLicencePlateRequest)
{
return TryExecuteTransactionalFuncAsync(async () =>
{
Guid authenticatedStaffMemberId = GetAuthenticatedStaffMemberId();
await CarService.SetCarColorAsync(authenticatedStaffMemberId, setCarColorAndLicencePlateRequest.CarId, setCarColorAndLicencePlateRequest.CarColor);
await CarService.SetLicencePlateColorAsync(authenticatedStaffMemberId, setCarColorAndLicencePlateRequest.CarId, setCarColorAndLicencePlateRequest.LicencePlateColor);
return Ok();
});
}
我服务的两种方法
public async Task SetColorAsync(Guid authenticatedStaffMemberId, Guid carId, Color color)
{
CarEntity carToUpdate = await CarRepository.GetByIdAsync(carId);
if (carToUpdate == null) throw new BusinessException($"Unknown user for the id : {carId}");
carToUpdate.UpdatedAt = DateTimeOffset.UtcNow;
carToUpdate.UpdatedBy = authenticatedStaffMemberId;
carToUpdate.Color = color;
UserRepository.Update(carToUpdate);
await CarRepository.SaveChangesAsync();
}
public async Task SetLicencePlateColorAsync(Guid authenticatedStaffMemberId, Guid carId, Color licencePlateColor)
{
CarEntity carToUpdate = await CarRepository.GetByIdAsync(carId);
if (carToUpdate == null) throw new BusinessException($"Unknown user for the id : {carId}");
carToUpdate.UpdatedAt = DateTimeOffset.UtcNow;
carToUpdate.UpdatedBy = authenticatedStaffMemberId;
carToUpdate.LicencePlateColor = licencePlateColor;
UserRepository.Update(carToUpdate);
await CarRepository.SaveChangesAsync();
}
当然,我只能在一个方法中完成它,但SetColor()和SetLicencePlateColor()可以单独调用,我不想保持2次相同的代码。
如果您尝试这段代码(通过将其包含在项目中)以重现该情况,您将看到第二个Update()
是抛出上述异常的那个。
因为我无法提供TryExecuteTransactionalFuncAsync
logic的完整代码,所以有一个简单的版本
public async Task<IHttpActionResult> TryExecuteTransactionalFuncAsync(Func<Task<IHttpActionResult>> apiTask)
{
using (var transaction = new DatabaseTransaction(DbContext.Database.BeginTransaction()))
{
var output = await apiTask.Invoke();
transaction.Complete();
return output;
}
}
好吧,我确实找到了解决方法!感谢https://www.itworld.com/article/2700950/development/a-generic-repository-for--net-entity-framework-6-with-async-operations.html
我刚把我的BaseRepository
改为:
public abstract class BaseRepository<T> : IBaseRepository<T> where T : class
{
/// <summary>
/// Context for the database
/// </summary>
protected readonly DataBaseContext DbContext;
protected readonly DbSet<T> DbSet;
// ...
public async Task<(T, int)> AddAsync(T entity, bool autoSaveChangesAsync = false)
{
Requires.ArgumentNotNullAndNotDefault(entity);
GetDbContext().Set<T>().Add(entity);
return (entity, await saveChangesAsync(autoSaveChangesAsync));
}
public async Task<(IEnumerable<T>, int)> AddAsync(IEnumerable<T> entities, bool autoSaveChangesAsync = false)
{
Requires.ArgumentNotNullAndNotDefault(entities);
var addedEntities = new List<T>();
foreach (var entity in entities)
addedEntities.Add((await AddAsync(entity, false)).Item1);
return (addedEntities, await saveChangesAsync(autoSaveChangesAsync));
}
public Task<T> GetByIdAsync(Guid id)
{
Requires.ArgumentNotNullAndNotDefault(id);
return DbSet.FindAsync(id);
}
// ...
public async Task<(T, int)> UpdateAsync(T entity, int key, bool autoSaveChangesAsync = false)
{
if (entity == null)
return (null, 0);
T existingEntity = await DbContext.Set<T>().FindAsync(key);
if (existingEntity != null)
{
DbContext.Entry(existingEntity).CurrentValues.SetValues(entity);
return (existingEntity, await saveChangesAsync(autoSaveChangesAsync));
}
return (existingEntity, 0); // Because 0 entity have been written into the database
}
// ...
private async Task<int> saveChangesAsync(bool autoSaveChangesAsync)
{
if (autoSaveChangesAsync)
return await SaveChangesAsync();
else
return 0; // Because 0 entity have been written into the database
}
public Task<int> SaveChangesAsync() => GetDbContext().SaveChangesAsync();
}
对于这个基础,我的密钥是
int
,但您可以将其更改为Guid
,这取决于您的实现
我希望它有帮助:)
以上是关于EF6 - Update()失败,多个SaveChangeAsync,但没有多个SaveChanges()的主要内容,如果未能解决你的问题,请参考以下文章