假设我有一个执行以下操作的 Controller 操作:
- 检查在特定时间是否有日历槽
- 检查是否没有与该时间段重叠的已预订约会
- 如果两个条件都满足,它会在给定时间创建一个新约会
简单的实现带来了多个问题:
- 如果在步骤 3 之前删除了在 1 中获取的日历槽怎么办?
- 如果在第 2 步之后但在第 3 步之前预约了另一个约会怎么办?
这些问题的解决方案似乎是使用 SERIALIZABLE 事务隔离级别。问题是每个人似乎都认为这种事务隔离级别非常危险,因为它可能会导致死锁。
给出以下简单的解决方案:
public class AController
{
// ...
public async Task Fn(..., CancellationToken cancellationToken)
{
var calendarSlotExists = dbContext.Slots.Where(...).AnyAsync(cancellationToken);
var appointmentsAreOverlapping = dbContext.Appointments.Where(...).AnyAsync(cancellationToken);
if (calendarSlotExists && !appointmentsAreOverlapping)
dbContext.Appointments.Add(...);
dbContext.SaveChangesAsync(cancellationToken);
}
}
始终防止并发问题的最佳方法是什么?我应该如何处理最终的死锁?
最佳答案
数据库完整性检查是你最好的 friend
根据您的描述,您的约会是基于时段的。这使问题变得简单得多,因为您可以有效地为 Appointments
表上的 SlotId
定义唯一约束。然后你需要一个用于 Appointments.SlotId
引用的外键 Slot.Id
what if the calendar slot fetched in 1 is removed before step 3?
数据库会抛出外键违规异常
what if another appointment is booked after step 2 but before step 3?
数据库会抛出重复键异常
接下来您需要做的是捕获这两个异常并将用户重定向回预订页面。再次从数据库重新加载数据并检查是否有无效条目,通知用户进行修改并重试。
对于死锁部分,它实际上取决于您的表结构。你访问数据的方式,你索引它们的方式,以及 DB 的查询计划。对此没有明确的答案。
关于c# - 在 Entity Framework 核心中执行业务规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55343663/