c# - 确保在实体框架请求期间阻止数据库行
问题描述
我创建了与我的数据库通信的服务。GetAvailableUserId
服务的方法不能同时运行,因为我不想为两个不同的调用返回相同的用户 ID。到目前为止,我已经做到了:
public class UserService : IUserService
{
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
using (var transaction = context.Database.BeginTransaction())
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
transaction.Commit();
return availableUser.Id;
}
}
}
}
我想测试服务是否会按预期工作,所以我创建了简单的控制台应用程序来模拟同步请求:
Parallel.For(1, 100, (i, state) => {
var service = new UserServiceReference.UserServiceClient();
var id = service.GetAvailableUserId();
});
不幸的是,它没有通过那个简单的测试。我可以看到,它为不同的for
迭代返回了相同的 id。
那里有什么问题?
解决方案
实现此目的的替代方法是捕获 DbUpdateConcurrencyException 以检查在您尝试保存时检索后行中的值是否已更改。
因此,例如,如果同一记录被检索两次。第一个在数据库中更新可用值将导致另一个在尝试保存时抛出并发异常,因为该值自检索以来已更改。
在实体中的可用属性上方添加 ConcurrencyCheck 属性。
[ConcurrencyCheck]
public bool Available{ get; set; }
然后:
public int GetAvailableUserId()
{
using (var context = new UsersEntities())
{
try
{
var availableUser = context.User
.Where(x => x.Available)
.FirstOrDefault();
if (availableUser == null)
{
throw new Exception("No available users.");
}
availableUser.Available = false;
context.SaveChanges();
return availableUser.Id;
}
catch (DbUpdateConcurrencyException)
{
//If same row was already retrieved and updated to false, do not save, instead call the method again to get the next true row.
return GetAvailableUserId();
}
}
}
推荐阅读
- python - Selenium:从下拉菜单中选择
- swift - 使用 touchesMoved 解决 FPS 下降问题?
- c# - 统一在循环中满足这两个条件,导致对象从左向右移动以“振动”
- sql - 如何自动生成模式(SQL 服务器)?
- android - kivy 需求问题 buildozer
- spring - 使用 Corda 运行 Spring 服务器时出现 Quasar 错误
- ios - 从 iOS 中的 UIWebView 中删除粗体、斜体和下划线选项
- linux - Fedora 28 / GLIBC 2.27 libm.so.6 logf() 和 powf() c++
- c# - C# 将 Selenium 与 Chrome 便携版和 driver.Navigate().GoToUrl 一起使用
- excel - 将某些单元格从更改数量的工作表复制到新行