实体框架,PostgreSQL,具有隐藏xmin列的乐观并发

时间:2012-06-29 07:32:46

标签: entity-framework postgresql optimistic-concurrency

我正在使用针对PostgreSQL 9.1数据库的Entity Framework(Model First方法)。

您可能都知道每个表都有一个名为 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" />

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

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

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

BTW xmin列数据类型为'xid',这是Npgsql ADO.NET提供程序所不知道的。

编辑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

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

编辑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.

如果有变化,我有以下内容(更新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 ......

编辑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属性。我很乐意分享我的代码。不要犹豫。

1 个答案:

答案 0 :(得分:2)

xmin不太可能很快消失,可用于实现更新的简单形式的乐观并发控制。小心内部它是一个无符号 32位数字,所以不要试图将它存储在有符号的32位字段中,否则它可能会工作一段时间然后严重破坏

只要您将其映射到适当的数据类型,请事先阅读它,将其包含在WHERE的{​​{1}}子句中,并对“未找到”条件采取适当的操作很简单。