我目前面临一种不确定的情况。对于每个存储库,我都有一个基础,这是它的一部分:
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个不同的值,并且每次{{1 }},我Update()
,它引发以下异常,但仅适用于异步逻辑:
附加类型为“ Project.Model.CarEntity”的实体失败,因为 相同类型的另一个实体已经具有相同的主键 值。使用“附加”方法或设置 如果实体中的任何实体处于“未更改”或“已修改”状态, 图的键值有冲突。这可能是因为某些实体 是新的,尚未收到数据库生成的键值。在 在这种情况下,请使用“添加”方法或“添加”实体状态进行跟踪 图形,然后将非新实体的状态设置为“不变”或 视情况进行“修改”。
有我的控制器:
SaveChanges()
我的2种服务方法
[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();
});
}
当然,我只能用一种方法来做到这一点,但是SetColor()和SetLicencePlateColor()可以分别调用,而我不想保持两次相同的代码。
如果您尝试这段代码(通过将其包含到项目中)来重现这种情况,则会看到第二个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();
}
是引发上述异常的代码。
因为我无法提供Update()
逻辑的完整代码,所以它有一个简单的版本
TryExecuteTransactionalFuncAsync
答案 0 :(得分:0)
好的,我确实找到了解决方法!感谢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
, 取决于您的实现
我希望这会有所帮助:)