我正在开发一个应用程序,它应该在收到PayPal即时支付通知时创建产品(如运输保险单)。不幸的是,贝宝有时会发送重复的通知。此外,还有另一个第三方在从paypal获得更新时同时执行web服务更新。
下面是所涉及的数据库表的基本图表。

// table "package"
// columns packageID, policyID, other data...
//
// table "insurancepolicy"
// columns policyID, coverageAmount, other data...

下面是我想做的基本图表:
using (SqlConnection conn = new SqlConnection(...))
{
  sqlTransaction sqlTrans = conn.BeginTransaction(IsolationLevel.RepeatableRead);

  // Calls a stored procedure that checks if the foreign key in the transaction table has a value.
  if (PackageDB.HasInsurancePolicy(packageID, conn))
  {
    sqlTrans.Commit();
    return false;
  }

  // Insert row in foreign table.
  int policyID = InsurancePolicyDB.Insert(coverageAmount, conn);
  if (policyID <= 0)
  {
    sqlTrans.Rollback();
    return false;
  }

  // Assign foreign key to parent table.  If this fails, roll back everything.
  bool assigned = PackageDB.AssignPolicyID(packageID, policyID, conn);
  if (!assigned)
  {
    sqlTrans.Rollback();
    return false;
  }
}

如果有两个(或更多)线程(或进程或应用程序)同时执行此操作,我希望第一个线程在没有policyID的情况下锁定“package”行,直到创建策略并将policyID分配给package表。然后,在将policyID分配给package表之后,锁将被释放。我希望调用同一代码的另一个线程在读取包行时会暂停,以确保它首先没有policyID。当第一个事务的锁被释放时,我希望第二个事务将看到policyID在那里,因此返回时不会在policy表中插入任何行。
注意:由于crud数据库的设计,每个存储过程涉及读取(选择)、创建(插入)或更新。
这是repeatableread事务隔离的正确用法吗?
谢谢。

最佳答案

如果insert into Policy在尝试插入duplicate时遇到一些惟一性表约束,则会更安全、更干净。提高隔离级别可能会降低并发性,并导致其他棘手的问题,如死锁。
另一种方法是始终插入策略行,如果包已附加到策略,则将其回滚:

begin tran (read committed)

/* tentatively insert new Policy */
insert Policy

/* attach Package to Policy if it's still free */
update Package
  set Package.policy_id = @policy_id
  where Package.package_id = @package_id and Package.policy_id is null

if @@rowcount > 0
  commit
else
  rollback

在很少发生冲突的情况下,这种方法最有效,这似乎是你的情况。

关于c# - IsolationLevel.RepeatableRead防止重复,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/124313/

10-11 23:19