.Net mvc EF代码首先如何处理对数据库的并发更新请求
.Net mvc EF codefirst how to hanle concurrent update requests to database
我在数据库中有一个 table:
USERID MONEY
______________
1 500
货币价值只能由拥有帐户的登录用户更改。我有一个像这样的函数:
bool buy(int moneyToSpend)
{
var moneyRow = db.UserMoney.Find(loggedinUserID);
if(moneyRow.MONEY < moneyToSpend)
return false;
//code for placing order
moneyRow.MONEY -= moneyToSpend;
return true;
}
我知道 mvc 会话总是同步的,所以在一个用户会话中永远不会有 2 个对此函数的同步调用。但是,如果用户从不同的浏览器登录站点 2 次怎么办?它仍然是单线程会话还是我可以获得 2 个对此函数的并发请求?
如果会有并发,那么我应该如何使用 EF 处理它?通常在 ADO 中,我会使用 MSSQL 的 "BEGIN WORK" 来处理这种情况,但我不知道如何使用 EF 来实现。
感谢您的宝贵时间!
我不认为这对你来说是个大问题,因为据我所知,MSSQL 不允许 asyncroneus 数据从同一个线程提交给同一个用户,它必须在移动之前完成一个进程到下一个,但你可以尝试这样的事情
using (var db = new YourContext())
{
var moneyRow = db.UserMoney.Find(loggedinUserID);
moneyRow.MONEY -= moneyToSpend;
bool saveFailed;
do
{
saveFailed = false;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update original values from the database
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
可以在这里找到更多内容 Optimistic Concurrency Patterns
我建议您使用 RowVersion
来处理并发请求。
// in UserMoney.cs
[Timestamp]
public byte[] RowVersion { get; set; }
// in model builder
modelBuilder.Entity<UserMoney>().Property(p => p.RowVersion).IsConcurrencyToken();
// The update logic
public bool Buy(int moneyToSpend, byte[] rowVersion)
{
try
{
var moneyRow = db.UserMoney.Find(loggedinUserID);
if(moneyRow.MONEY < moneyToSpend)
{
return false;
}
//code for placing order
moneyRow.MONEY -= moneyToSpend;
return true;
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var submittedUserMoney = (UserMoney) entry.Entity;
var databaseValue = entry.GetDatabaseValues();
if (databaseValue == null)
{
// this entry is no longer existed in db
}
else
{
// this entry is existed and have newer version in db
var userMoneyInDb = (UserMoney) databaseValue.ToObject();
}
}
catch (RetryLimitExceededException)
{
// probably put some logs here
}
}
我在数据库中有一个 table:
USERID MONEY
______________
1 500
货币价值只能由拥有帐户的登录用户更改。我有一个像这样的函数:
bool buy(int moneyToSpend)
{
var moneyRow = db.UserMoney.Find(loggedinUserID);
if(moneyRow.MONEY < moneyToSpend)
return false;
//code for placing order
moneyRow.MONEY -= moneyToSpend;
return true;
}
我知道 mvc 会话总是同步的,所以在一个用户会话中永远不会有 2 个对此函数的同步调用。但是,如果用户从不同的浏览器登录站点 2 次怎么办?它仍然是单线程会话还是我可以获得 2 个对此函数的并发请求? 如果会有并发,那么我应该如何使用 EF 处理它?通常在 ADO 中,我会使用 MSSQL 的 "BEGIN WORK" 来处理这种情况,但我不知道如何使用 EF 来实现。
感谢您的宝贵时间!
我不认为这对你来说是个大问题,因为据我所知,MSSQL 不允许 asyncroneus 数据从同一个线程提交给同一个用户,它必须在移动之前完成一个进程到下一个,但你可以尝试这样的事情
using (var db = new YourContext())
{
var moneyRow = db.UserMoney.Find(loggedinUserID);
moneyRow.MONEY -= moneyToSpend;
bool saveFailed;
do
{
saveFailed = false;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update original values from the database
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
可以在这里找到更多内容 Optimistic Concurrency Patterns
我建议您使用 RowVersion
来处理并发请求。
// in UserMoney.cs
[Timestamp]
public byte[] RowVersion { get; set; }
// in model builder
modelBuilder.Entity<UserMoney>().Property(p => p.RowVersion).IsConcurrencyToken();
// The update logic
public bool Buy(int moneyToSpend, byte[] rowVersion)
{
try
{
var moneyRow = db.UserMoney.Find(loggedinUserID);
if(moneyRow.MONEY < moneyToSpend)
{
return false;
}
//code for placing order
moneyRow.MONEY -= moneyToSpend;
return true;
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var submittedUserMoney = (UserMoney) entry.Entity;
var databaseValue = entry.GetDatabaseValues();
if (databaseValue == null)
{
// this entry is no longer existed in db
}
else
{
// this entry is existed and have newer version in db
var userMoneyInDb = (UserMoney) databaseValue.ToObject();
}
}
catch (RetryLimitExceededException)
{
// probably put some logs here
}
}