我试图为EF实施乐观锁定模型。我在表中尝试使用时间戳数据类型的列。
我尝试通过修改T4脚本在成员上添加Timestamp
属性。运行我的测试并看到它不起作用,然后我尝试以相同的方式在同一成员上添加ConcurrencyCheck
属性(通过T4)。但是,在这两种情况下,生成的SQL实际上都不会在更新时使用该成员(如下所示)。我还在EDMX设计师的财产上设置了Concurrency Mode
到Fixed
。
我可以在更新后的单独选择中看到timestamp列,但不能在更新本身上看到。我试过打破SaveChanges()
,更改数据库记录并在手动更新后看到时间戳值发生变化,执行SaveChanges()
行并且执行正常而不抛出异常。我是否缺少某些东西,是否需要滚动自己的时间戳比较代码,还是需要进入EF拦截器世界来更改SQL输出?感谢。
生成的SQL :(通过DbContext.Database.Log
登录)
UPDATE [dbo].[Foo]
SET [FooTitle] = @0
WHERE ([FooId] = @1)
SELECT [fooversion]
FROM [dbo].[Foo]
WHERE @@ROWCOUNT > 0 AND [FooId] = @1
T4代后成员的最终版本:
[Timestamp]
[ConcurrencyCheck]
public byte[] fooversion { get; set; }
我试图让EF做的事情:
UPDATE [dbo].[Foo]
SET [FooTitle] = @0
WHERE ([FooId] = @1 AND [fooversion] = @2)
答案 0 :(得分:0)
我决定推出自己的检查代码。虽然它确实需要两次访问数据库,但效率非常高。
我写了一个接口,它附加了一系列我已经用来将东西附加到我的实体的部分类。它定义了一个查找选择器,我的实体基类服务可以用它来获取特定的版本。没有结果?返回一条消息。如果有结果,那么实体就是您期望的更新。
界面:
public interface IVersionableDbObject<TEntity>
where TEntity : class, IValidatableObject
{
byte[] Version { get; }
Expression<Func<TEntity, bool>> LookupSelector { get; }
}
一个实现示例:
public partial class Foo : IVersionableDbObject<Foo>
{
public byte[] Version { get { return fooversion; } }
public Expression<Func<Foo, bool>> LookupSelector
{
get { return foo => foo.FooId == FooId && foo.fooversion == Version; }
}
}
基本服务中的检查代码:
IVersionableDbObject<TEntity> versionableSource = entity as IVersionableDbObject<TEntity>;
if (versionableSource != null)
{
bool versionExists = innerContext.Set<TEntity>().Where(versionableSource.LookupSelector).Any();
if (!versionExists)
return new ValidationResult(string.Format(FooResources.EntityUpdateVersionConflictError,
BitConverter.ToInt32(versionableSource.Version, 0)));
}
验证结果只是一条消息,它是一个格式化程序,接受试图查找的版本。返回ValidationResult
特定于我自己的实现。如果您使用它,请按照您的意愿行事。
这里是.Any()
上生成的SQL:
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Foo] AS [Extent1]
WHERE ([Extent1].[FooId] = @p__linq__0) AND ([Extent1].[fooversion] = @p__linq__1)
)) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
有趣的是,当更新实际发生时,EF仍会刷新行版本,而不会使用类似于我在OP中发布的SQL的Timestamp
或ConcurrencyCheck
属性。