确保 Entity Framework 中的并发(金钱)交易?
Ensure concurrent (money) transactions in Entity Framework?
假设我有一个 account_profile
table,它有一个类似于账户资金的 Score
字段(数据库类型是 BIGINT(20)
并且 EntityFramework 类型是 long
,因为我不需要小数)。现在我有以下功能:
public long ChangeScoreAmount(int userID, long amount)
{
var profile = this.Entities.account_profile.First(q => q.AccountID == userID);
profile.Score += amount;
this.Entities.SaveChanges();
return profile.Score;
}
不过,我怕ChangeScoreAmount
同时调用多次,最后的金额不对。
以下是我目前想到的解决方案:
在ChangeScoreAmount
函数中添加一个带有静态锁定变量的锁,因为class本身可能在需要时被实例化多次。它看起来像这样:
public long ChangeScoreAmount(int userID, long amount)
{
lock (ProfileBusiness.scoreLock)
{
var profile = this.Entities.account_profile.First(q => q.AccountID == userID);
profile.Score += amount;
this.Entities.SaveChanges();
return profile.Score;
}
}
问题是,我从来没有尝试过对 static
变量加锁,所以我不知道它是否真的安全,是否会发生死锁。此外,如果在此函数之外的其他地方,中途应用对 Score
字段的更改,则可能会很糟糕。
好的,这不再是一个选项,因为我的服务器应用程序将在多个站点上 运行,这意味着无法使用锁定变量
- 正在数据库中创建存储过程并在函数中调用该存储过程。但是,我不知道是否有 "atomic" 方法来创建该存储过程,以便一次只能调用一次,因为我仍然需要检索值,更改它然后再次更新它?
我正在使用 MySQL Community 5.6.24 和 MySQL .NET Connector 6.9.6 以防万一。
注意 我的服务器应用程序可能 运行 连接在多台服务器计算机上。
您可以使用具有可重复读取隔离级别的 sql 事务,而不是锁定应用程序。例如你可以写
public long ChangeScoreAmount(int userID, long amount)
{
using(var ts = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })
{
var profile = this.Entities.account_profile.First(q => q.AccountID == userID);
profile.Score += amount;
this.Entities.SaveChanges();
ts.Complete();
return profile.Score;
}
}
事务保证当您不提交或回滚时,accountprofile 记录不会在数据库中更改。
假设我有一个 account_profile
table,它有一个类似于账户资金的 Score
字段(数据库类型是 BIGINT(20)
并且 EntityFramework 类型是 long
,因为我不需要小数)。现在我有以下功能:
public long ChangeScoreAmount(int userID, long amount)
{
var profile = this.Entities.account_profile.First(q => q.AccountID == userID);
profile.Score += amount;
this.Entities.SaveChanges();
return profile.Score;
}
不过,我怕ChangeScoreAmount
同时调用多次,最后的金额不对。
以下是我目前想到的解决方案:
在
ChangeScoreAmount
函数中添加一个带有静态锁定变量的锁,因为class本身可能在需要时被实例化多次。它看起来像这样:public long ChangeScoreAmount(int userID, long amount) { lock (ProfileBusiness.scoreLock) { var profile = this.Entities.account_profile.First(q => q.AccountID == userID); profile.Score += amount; this.Entities.SaveChanges(); return profile.Score; } }
问题是,我从来没有尝试过对 static
变量加锁,所以我不知道它是否真的安全,是否会发生死锁。此外,如果在此函数之外的其他地方,中途应用对 Score
字段的更改,则可能会很糟糕。
好的,这不再是一个选项,因为我的服务器应用程序将在多个站点上 运行,这意味着无法使用锁定变量
- 正在数据库中创建存储过程并在函数中调用该存储过程。但是,我不知道是否有 "atomic" 方法来创建该存储过程,以便一次只能调用一次,因为我仍然需要检索值,更改它然后再次更新它?
我正在使用 MySQL Community 5.6.24 和 MySQL .NET Connector 6.9.6 以防万一。
注意 我的服务器应用程序可能 运行 连接在多台服务器计算机上。
您可以使用具有可重复读取隔离级别的 sql 事务,而不是锁定应用程序。例如你可以写
public long ChangeScoreAmount(int userID, long amount)
{
using(var ts = new TransactionScope(TransactionScopeOption.RequiresNew,
new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead })
{
var profile = this.Entities.account_profile.First(q => q.AccountID == userID);
profile.Score += amount;
this.Entities.SaveChanges();
ts.Complete();
return profile.Score;
}
}
事务保证当您不提交或回滚时,accountprofile 记录不会在数据库中更改。