2

我有一个 WebAPI OData 控制器,它使用 Delta 对我的实体进行部分更新。

在我的实体框架模型中,我有一个版本字段。这是 SQL Server 数据库中的行版本,映射到实体框架中的字节数组,其并发模式设置为固定(它首先使用数据库)。

我正在使用 fiddler 使用 Version 字段的陈旧值发回部分更新。我从上下文中加载当前记录,然后在顶部修补更改的字段,这会更改版本列中的值而不会引发错误,然后当我在上下文中保存更改时,所有内容都会保存而不会出现错误。显然这是意料之中的,正在保存的实体尚未从上下文中分离出来,所以我如何使用 Delta 实现乐观并发。

我正在使用所有东西的最新版本(或者就在圣诞节前),所以实体框架 6.0.1 和 OData 5.6.0

public IHttpActionResult Put([FromODataUri]int key, [FromBody]Delta<Job> delta)
{
    using (var tran = new TransactionScope())
    {
        Job j = this._context.Jobs.SingleOrDefault(x => x.JobId == key);

        delta.Patch(j);

        this._context.SaveChanges();

        tran.Complete();

        return Ok(j);
    }
}

谢谢

4

3 回答 3

3

我也刚刚使用 Entity Framework 6 和 Web API 2 OData 控制器遇到了这个问题。

EF DbContext 似乎使用在 PUT/PATCH 方法开始时加载实体时获得的时间戳的原始值进行后续更新时的并发检查。

在保存更改之前将时间戳的当前值更新为与数据库中不同的值不会导致并发错误。

我发现您可以通过将时间戳的原始值强制为上下文中的当前值来“修复”此行为。

例如,您可以通过在上下文中覆盖 SaveChanges 来做到这一点,例如:

public partial class DataContext
{
    public override int SaveChanges()
    {
        foreach (DbEntityEntry<Job> entry in ChangeTracker.Entries<Job>().Where(u => u.State == EntityState.Modified))
            entry.Property("Timestamp").OriginalValue = entry.Property("Timestamp").CurrentValue;

        return base.SaveChanges();
    }
}

(假设并发列被命名为“Timestamp”并且该列的并发模式在 EDMX 中设置为“Fixed”)

对此的进一步改进是编写自定义界面并将其应用于需要此修复的所有模型,并将“作业”替换为上面代码中的界面。

来自实体框架团队的 Rowan 的反馈(2015 年 8 月 4 日):

这是设计使然。在某些情况下,更新并发令牌是完全有效的,在这种情况下,我们需要当前值来保存它应该设置的值,并需要原始值来包含我们应该检查的值。例如,您可以将 Person.LastName 配置为并发令牌。这是此操作中使用的“查询和更新”模式的缺点之一。

您为设置正确的原始值而添加的逻辑是在这种情况下使用的正确方法。

于 2015-07-30T10:33:13.557 回答
1

当您将数据发布到服务器时,您还需要发送RowVersion字段。如果您使用 fiddler 对其进行测试,RowVersion请从数据库中获取最新值并将该值添加到您的Request Body.

应该是这样的;

RowVersion: "AAAAAAAAB9E="

如果它是一个网页,当您从服务器加载数据时,再次RowVersion从服务器获取字段,将其保存在隐藏字段中并将其与其他更改一起发送回服务器。

基本上,当你调用PATCH方法时,RowField需要在你的patch对象中。

然后像这样更新您的代码;

Job j = this._context.Jobs.SingleOrDefault(x => x.JobId == key);

// Concurrency check
if (!j.RowVersion.SequenceEqual(patch.GetEntity().RowVersion))
{
    return Conflict();
}

this._context.Entry(entity).State = EntityState.Modified; // Probably you need this line as well?
this._context.SaveChanges();
于 2014-05-06T12:21:17.090 回答
0

简单,你总是用 Entity Framework 做的方式:你添加一个字段Timestamp并将该字段放入. 这确保 EF 知道此时间戳字段不是任何查询的一部分,而是用于确定版本控制。Concurrency ModeFixed

另请参阅http://blogs.msdn.com/b/alexj/archive/2009/05/20/tip-19-how-to-use-optimistic-concurrency-in-the-entity-framework.aspx

于 2014-01-06T14:46:59.287 回答