【问题标题】:Why does an insert using EF code first fail within a TransactionScope?为什么使用 EF 代码的插入首先在 TransactionScope 中失败?
【发布时间】:2023-03-21 07:30:02
【问题描述】:

我有一种情况,我在一个表 (asset_type) 中创建一条记录,并通过第二个表 (asset) 中的外键引用它。在这种情况下,这两个插入都发生在同一个 TransactionScope 内。

使用原始 DbConnection 时,插入成功:

conn.ConnectionString = "host=localhost;port=5432;database=test_client_alpha;user id=tcauser;password=tcapw";    

using (var trans = new TransactionScope())
{
  conn.Open();
  conn.EnlistTransaction(Transaction.Current);

  var cmd = conn.CreateCommand();
  cmd.CommandText = "INSERT INTO overview.asset_type ( name ) VALUES( 'Unknown' ) RETURNING id";
  var assetTypeId = (int)cmd.ExecuteScalar();

  cmd.CommandText = string.Format("INSERT INTO overview.asset "
                                  + "(asset_type_id, client_id, is_active, is_gps_active, is_virtual, default_lon, default_lat) "
                                  + "VALUES ({0}, 'mid', TRUE, TRUE, FALSE, 0, 0 ) "
                                  + "RETURNING id ", assetTypeId);
  var assetId = (int)cmd.ExecuteScalar();

  trans.Complete();
}

但是,如果我切换到使用 DbContext 类,则第二次插入(到资产中)会失败并违反外键约束,就好像第一次插入(到资产类型中)没有发生一样:

conn.ConnectionString = "host=localhost;port=5432;database=test_client_alpha;user id=tcauser;password=tcapw";

using (var trans = new TransactionScope())
{
  using (var context = new TestContext(conn, false))
  {
    var assetTypeId = context.Database
      .SqlQuery<int>("INSERT INTO overview.asset_type ( name ) VALUES( 'Unknown' ) RETURNING id")
      .Single();

    var assetId = context.Database
      .SqlQuery<int>(string.Format("INSERT INTO overview.asset "
                                    + "(asset_type_id, client_id, is_active, is_gps_active, is_virtual, default_lon, default_lat) "
                                    + "VALUES ({0}, 'mid', TRUE, TRUE, FALSE, 0, 0 ) "
                                    + "RETURNING id ", assetTypeId))
      .Single();
    trans.Complete();
  }
}

如果我删除 TransactionScope,则 DbContext 示例将正常执行。

我尝试过使用 IsolationLevel 设置(ReadCommitted、ReadUncommitted)但没有成功。

我意识到在此示例中我不需要 TransactionScope。这是涉及与多个数据库交互并需要分布式事务的较大代码块的一部分。

我的数据库是 PostgreSQL,我正在使用 DevArt 的 dotConnect .NET 驱动程序。

有人知道为什么 DbContext 示例不起作用吗?

【问题讨论】:

  • 我从来没有真正使用过 DbContext 的原始 SQL 方法,但 SqlQuery 不是仅用于 READ 操作(查询),ExecuteSqlCommand 用于 DML,如 INSERT、UPDATE、DELETE?但是我很惊讶你没有得到例外。
  • 我用 SQL Server 和 SQLClient 作为提供者测试了类似的代码(但相同的逻辑:在 table1 中插入 row1,然后在 table2 中插入 row2,FK 到 row1)。它在有和没有TransactionScope 的情况下都有效。问题可能与 PostgreSQL 或 dotConnect 驱动程序有关。

标签: c# .net postgresql transactions entity-framework-4.1


【解决方案1】:

Managing Connections and Transactions:

实体框架仅在需要时打开连接,例如执行查询或调用 SaveChanges,然后在操作完成时关闭连接。

  • 调用以下任何方法都会打开连接:
  • 在 ObjectContext 上保存更改或刷新。
  • FirstOrDefault,或 ObjectQuery 上的 First。
  • 在 EntityCollection 上加载。
  • 在 EntityReference 上加载。
  • 任何语言集成查询 (LINQ) 方法或 ObjectQuery 查询构建器方法,例如 Where、OrderBy 或 Select。

然后它打开另一个连接,事务范围抛出异常。你必须设置Distributed Transaction Coordinator(不能说它是真实的还是不是PostgreSQL的)。

如果它是真实的,那么在设置 DTC 之后,只需在范围内打开您的 conn 对象。

【讨论】:

  • 我明白了,所以在上下文范围内手动管理连接生命周期?
  • @Chris Hogan 是的,如果需要交易范围。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-06
  • 1970-01-01
  • 2016-06-06
  • 1970-01-01
  • 2011-06-03
  • 2014-07-06
相关资源
最近更新 更多