我正在开发一个应在收到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;
}
}
如果有两个(或多个)线程(或进程或应用程序)同时执行此操作,则我希望第一个线程在没有策略ID的情况下锁定“程序包”行,直到创建策略并分配了策略ID到包装表。然后,在将policyID分配给包表之后,将释放该锁。我希望正在调用相同代码的另一个线程在读取包行时暂停,以确保它首先没有policyID。释放第一个事务的锁时,我希望第二个事务将看到policyID存在,因此返回而无需在策略表中插入任何行。
注意:由于CRUD数据库的设计,每个存储过程都涉及“读取”(选择),“创建”(插入)或“更新”。
这是对RepeatableRead事务隔离的正确使用吗?
如果insert into Policy只是在尝试插入重复项时遇到一些唯一性表约束,那将更安全,更清洁。提高隔离级别会降低并发性,并导致其他令人讨厌的问题,例如死锁。
另一种方法是始终插入“策略”行,然后在“包”已附加到“策略”的情况下回滚:
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
当您很少遇到冲突时,这种方法最有效。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。