c# - 事务无法通过 Task.WhenAll 处理并行命令
问题描述
我的 Postgres DB 中有一些主表(如 Companies)和许多从属表(如 CompanyAddresses、CompanyPaymentInfos 等):
CREATE TABLE Companies (
Id uuid NOT NULL PRIMARY KEY,
...);
CREATE TABLE CompanyAddresses(
CompanyId uuid NOT NULL PRIMARY KEY REFERENCES Companies(Id),
...);
CREATE TABLE CompanyPaymentInfos(
CompanyId uuid NOT NULL PRIMARY KEY REFERENCES Companies(Id),
...);
我在 C# 代码中使用标准库中的事务:
private TransactionScope GeTransactionScope()
{
return new TransactionScope(
TransactionScopeOption.RequiresNew,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted
},
TransactionScopeAsyncFlowOption.Enabled);
}
private async Task DoChange(...)
{
using (var scope = GeTransactionScope())
{
await Insert(Company);
await Task.WhenAll(
Insert(CompanyPaymentInfo),
Insert(CompanyAddress),
Insert(CompanyTags),
// so on
);
scope.Complete();
}
}
每个Insert
命令仅生成 SQL 代码的执行,没有任何内部事务。
在执行 DoChange 后,我收到此错误:
Npgsql.PostgresException(0x80004005):23503:在表“companyaddresses”上插入或更新违反外键约束“companyaddresses_companyid_fkey”
当然,我有很多问题,例如:
- 为什么我会收到错误消息?
- 为什么我在插入 CompanyAddress 时收到错误,而不是 CompanyPaymentInfo?
如果我DoChange
改为顺序执行,一切正常:
private void DoChange()
{
using (var scope = GeTransactionScope())
{
await Insert(Company);
await Insert(CompanyPaymentInfo);
await Insert(CompanyAddress);
await Insert(CompanyTags);
// ...
scope.Complete();
}
}
也许它有帮助:
- 我使用网络核心 2.0
- 我使用具有标准设置的 Postgres 10.4(ReadCommitted 作为隔离级别等)。此外,我已添加
enlist=true
到我的连接字符串以使交易正常工作。 - 我在
Insert
命令中使用 Npgsql 3.2.5 和 Dapper 1.50.2
解决方案
这里没有什么神奇之处,您会收到错误消息,因为您在插入 CompanyAddress 时使用的连接不是您认为的那个。
这是一个新的连接。运行 ComapnyPaymentInfo 插入时,您正在使用已绑定到您的交易的连接。它正在等待新命令,因为您已在上一步中等待。
另一方面,使用 Task.WhenAll() 将尝试使用多个线程。如果一个连接正忙于运行一个命令,它将不会被使用并且会产生一个新的。
请记住,在使用事务时,您只有一个可用连接,您无法从并行性中受益。
推荐阅读
- nlp - 防止 Luis.ai 将“a”或“the”识别为实体
- snowflake-cloud-data-platform - 雪花 - 在 SELECT 上引用别名
- r - 如何使用图像作为图例键字形?
- javascript - Jest spy 在不同的目录中不起作用?
- python-3.x - discord.py - 需要帮助才能使用 cog_command_error
- python - rio.plot.show 带彩条?
- np - 为什么所有 NP 完全问题都可以简化为 3-SAT?
- https - “443 端口似乎已关闭”Elastic Beanstalk 负载均衡器
- java - 如何访问没有应用程序 ID 的默认应用程序?
- mysql - 确定用户的新分数是否是他们在特定活动中获得的最佳分数