【问题标题】:Entity Framework, PostgreSQL, Optimistic Concurrency with hidden xmin column实体框架、PostgreSQL、带有隐藏 xmin 列的乐观并发
【发布时间】:2016-01-28 21:29:00
【问题描述】:

我正在针对 PostgreSQL 9.1 数据库使用实体框架(模型优先方法)。

您可能都知道每个表都有一个名为 xmin 的隐藏列,我将使用它来帮助 EF 在执行更新之前确定该行是否已更改。

我知道 PostgreSQL 的内部结构可能会发生变化,这对于生产代码来说可能根本不是一个好主意,但我想尝试一下。

手动更新模型以在一个表中包含此列并测试其行为所需的步骤是什么?

TIA。

编辑 1 :这是我目前的情况,使用带有自我跟踪实体的模型优先方法。

模型已修改如下:

CSDL : <Property Name="xmin" Type="Decimal" Nullable="false" Precision="15" Scale="0" ConcurrencyMode="Fixed" />
SSDL : <Property Name="xmin" Type="numeric" Nullable="false" Precision="15" Scale="0" StoreGeneratedPattern="Computed" />

xmin 列在 SELECT 上有效检索,然后在 UPDATE 期间使用:

UPDATE "schema"."mytable" SET "column1"=FALSE WHERE ("pk"=cast(7526 as numeric)) AND ("xmin"=cast(1185877 as numeric))

这里的问题在于 xmin 列上使用的演员表。它失败。到目前为止,我发现它根本不需要演员表。但我不知道如何告诉 EF 在此列的查询中完全不使用强制转换。

顺便说一句,xmin 列数据类型是 'xid',这对于 Npgsql ADO.NET 提供程序来说是未知的。

EDIT 2:这是生成 SQL 文本的提供程序。查看提供程序代码后,我可以确认使用整数数据类型时没有强制转换。所以,我在我的实体中使用了一个整数来存储值并尝试选择和更新实体。它因并发异常而失败。

System.Data.OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
   at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
   at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)

pgsql 日志如下:

CEST LOG:  instruction : BEGIN; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
CEST LOG:  instruction : UPDATE "schema"."mytable" SET "column1"=TRUE WHERE ("pk"=cast(7526 as numeric)) AND ("xmin"=1185882)
CEST LOG:  instruction : ROLLBACK

嗯,检索受影响的行可能有问题,因为如果手动执行相同的查询,我更新了一行...

EDIT 3:我启用了 npgsql 调试日志。 当 xmin 没有改变时,我有以下(UPDATE 1):

7/2/2012 12:00:38 PM    12380   Debug   Entering NpgsqlState.ProcessBackendResponses()
7/2/2012 12:00:38 PM    12380   Debug   Entering PGUtil.ReadString()
7/2/2012 12:00:38 PM    12380   Debug   Get NpgsqlEventLog.LogLevel
7/2/2012 12:00:38 PM    12380   Debug   String read: UPDATE 1.

当它发生变化时,我有以下内容(UPDATE 0):

7/2/2012 1:50:06 PM 12700   Debug   Entering NpgsqlState.ProcessBackendResponses()
7/2/2012 1:50:06 PM 12700   Debug   Entering PGUtil.ReadString()
7/2/2012 1:50:06 PM 12700   Debug   Get NpgsqlEventLog.LogLevel
7/2/2012 1:50:06 PM 12700   Debug   String read: UPDATE 0.

但不幸的是,在这两种情况下我都有一个 OptimisticConcurrencyException...

EDIT 4:查看 npgsql 日志后,似乎实体框架在内部使用 DbCommand.ExecuteReader(CommandBehavior.SequentialAccess) 后跟 reader.Read() 来确定受影响的行数通过 UPDATE 语句。

我可能错了,但提供程序在 ForwardsOnlyDataReader.Read() 期间返回 false,这可能是问题的根源。

编辑 5:这绝对是提供商问题(版本 2.0.11.93 和即将发布的版本 2.0.11.94)。

对于 INSERT 语句,提供程序仅在基础数据类型为 SERIAL (int) 或 BIGSERIAL (bigint) 时支持计算列。

对于 UPDATE 语句,提供程序不处理给定 DbUpdateCommandTree 的 Returning 属性。这意味着不会返回任何计算列。您必须在调用 SaveChanges() 后手动刷新实体对象。 目前还不支持乐观并发。

我将尝试在提供程序中实施最低限度的支持,并及时通知您。

编辑 6:Returning 属性现在在我自己的提供程序版本中处理。我很乐意分享我的代码。不要犹豫,问。

【问题讨论】:

  • 关于转换 xmin 的问题:虽然它的内部实现是无符号 32 位整数,但它没有被放入数字类型系列中,因为它有一些不寻常的属性——比如循环值范围环绕。当值环绕时,天真地使用除相等性之外的比较肯定会中断,因此请确保不要这样做(或对它做出任何其他假设,即它不是非静态 ID)。临时转换为text 应该允许您将其转换为bigintnumeric。或者只是将其用作text
  • @kgrittn 上面显示的 SQL 文本 AND ("xmin"=1185882) 可以工作还是应该改为 AND ("xmin"='1185882')
  • 两者都应该工作,但引用的版本可能更好,因为签名与未签名的问题。在 PostgreSQL 中,带引号的文字不被视为字符串文字;它是unknown 类型的文字,可以隐式用作任何类型,包括xid。如果您使用变量而不是文字,您可能希望显式转换为 xid,如果需要,首先转换为 text

标签: entity-framework postgresql optimistic-concurrency


【解决方案1】:

xmin 不太可能很快消失,并且可以用于实现一种简单形式的乐观并发控制更新。请注意,它在内部是一个 无符号 32 位数字,因此不要尝试将其存储在带符号的 32 位字段中,否则它可能会工作一段时间然后严重损坏.

只要将其映射到适当的数据类型,预先阅读,将其包含在UPDATEWHERE 子句中,并对“未找到”情况采取适当的措施,这非常简单。

【讨论】:

  • 谢谢。我将编辑我的问题以添加有关我对该主题的研究的信息。
  • 对于 EF6 上的任何人,以下文章将有助于理解和解决此问题 - codeproject.com/Articles/817432/…
猜你喜欢
  • 2011-02-23
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
  • 2017-03-20
  • 2014-04-02
  • 1970-01-01
相关资源
最近更新 更多