#c# #postgresql #entity-framework #entity-framework-core
#c# #postgresql #entity-framework #entity-framework-core
Вопрос:
У меня есть следующие объекты, которые существуют как сущности в моем DbContext, подключенные к базе данных PostgreSQL:
public class Absence
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
public AbsenceReason AbsenceReason { get; set; }
}
public class AbsenceReason
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public bool IsDeleted { get; set; }
public List<Absence> Absences { get; set; }
}
Каждый объект, очевидно, больше, я отредактировал их, чтобы включить только то, что я считаю соответствующими частями. Наши требования включают в себя то, что причины отсутствия могут быть мягко удалены, оставаясь при этом привязанными к существующим отсутствиям. Причины отсутствия должны быть жестко удалены, если не осталось отсутствий, которые ссылаются на них.
Это отлично работает, когда я удаляю последнее отсутствие, которое ссылается на мягкую удаленную причину отсутствия. Однако у меня возникают проблемы, когда отсутствие обновляется, чтобы больше не ссылаться на мягкую удаленную причину отсутствия.
С моего контроллера в том же контексте, который я запускаю:
DbSet.Remove(absenceReason);
DbSet.Update(absence);
await _context.SaveChangesAsync();
Не имеет значения, в каком порядке выполняются строки удаления и обновления кода. Я дважды проверил этот факт, и все, что я прочитал, говорит мне, что EF выбирает порядок, в котором инструкции SQL должны выполняться самостоятельно. Проблема в том, что сгенерированный SQL, очевидно, не будет работать:
DELETE FROM "AbsenceReasons"
WHERE "Id" = @p0;
UPDATE "Absences" SET "AbsenceReasonId" = @p1
WHERE "Id" = @p10;
EF решает, что правильно сначала попытаться удалить причину отсутствия, прежде чем обновлять отсутствие, чтобы больше не ссылаться на причину отсутствия. В результате получается, что Microsoft.EntityFrameworkCore.Возникает исключение DbUpdateConcurrencyException.
Как я могу решить эту проблему, сохраняя атомарность?
Примечание: использование EF Core 5.0.1
Комментарии:
1. Имеет ли значение атомарность для этого? Если нет, почему бы не выполнить обновление, сохранить, удалить, сохранить
2. @CaiusJard Я понимаю, что это сработало бы, но я не хочу жертвовать атомарностью ради чего-то, что должно быть относительно простым в выполнении, на всякий случай, если в будущем возникнет проблема.
3. @NicholasVerstegen
SaveChanges
сохраняет все ожидающие изменения с помощью внутренней транзакции. Проблема здесь в коде контроллера, а не в том, как ядро EF упорядочивает изменения. Сгенерированный SQL будет работать просто отлично.UPDATE
Ничего не изменится. Этот порядок запрашивал код контроллера — aDELETE
, за которым следуетUPDATE
4. @PanagiotisKanavos Как я указал в вопросе, я проверил это, и порядок в контроллере не имеет значения. Выполнение
DbSet.Update(absence);
первого иDbSet.Remove(absenceReason);
второго дает тот же результат.
Ответ №1:
Я смог выполнить это с помощью TransactionScope, чтобы принудительно установить правильный порядок и сохранить атомарность.
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
DbSet.Update(absence);
await _context.SaveChangesAsync();
DbSet.Remove(absenceReason);
await _context.SaveChangesAsync();
scope.Complete();
}