实体框架乐观并发异常未发生

6
我们有一个使用EF4作为数据访问层的ASP.Net MVC应用程序,我们看到了一些意外的行为,涉及到OptimisticConcurrencyExceptions没有在我们认为它们应该被抛出的情况下被抛出。
我们已经将问题简化为以下代码...
   using System.Linq;
    using Project.Model;

    namespace OptimisticConcurrency
    {
        class Program
        {
            static void Main()
            {
                Contact firstContact = null;
                using (var firstEntities = new ProjectEntities())
                {
                    firstContact = (from c in firstEntities.Contacts 
                       where c.LastName == "smith" select c).Single();
                }

                using (var secondEntities = new ProjectEntities())
                {
                    var secondContact = (from c in secondEntities.Contacts 
                       where c.LastName == "smith" select c).Single();

                    secondContact.Title = "a";
                    secondEntities.SaveChanges();
                }

                firstContact.Title = "b";

                using (var thirdEntities = new ProjectEntities())
                {
                    var thirdContact = (from c in thirdEntities.Contacts 
                       where c.LastName == "smith" select c).Single();

                    thirdContact.Title = firstContact.Title;

                    //EXPLICITLY SET VERSION HERE
                    thirdContact.Version = firstContact.Version;  

                    thirdEntities.SaveChanges();
                }
            }
        }
    }

这是我们的MVC应用程序中发生的简单版本,但同样的问题也会出现。
当我们在thirdEntities上调用SaveChanges时,我期望会出现异常,但实际上没有任何异常被抛出。
更有趣的是,当我们附加SQL Profiler时,我们看到Version被用作where子句,但使用的是thirdEntities Version值(数据库中的当前值),而不是firstEntities的值,尽管它是在调用SaveChanges之前明确设置的。 SaveChanges正在将Version重置为检索到的值而不是设置的值。
在EDMX中,Version被设置为具有StoreGeneratedPattern设置为Computed。
有人知道这里发生了什么吗?
1个回答

10

这是一个问题。一旦将列设置为Computed,您就无法在应用程序中设置其值(实际上可以设置,但不会使用该值)。

编辑:

如果从数据库加载实体,则默认情况下将通过上下文进行跟踪。上下文存储其原始值。原始值例如用于快照更改跟踪,但它们也用作Computed属性的唯一有效来源。如果在实体中设置了Computed属性,则不会使用该值,而是使用原始值。解决方法是在修改任何其他内容之前修改原始值:

using (var context = new TestEntities())
{
    var entityToUpdate = context.MyEntities.Single(e => e.Id == someId);
    entityToUpdate.Timestamp = entity.Timestamp;

    ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(entityToUpdate);
    entry.ApplyOriginalValues(entityToUpdate);

    // set modified properties
    context.SaveChanges();
}

编辑2:

顺便说一下,一旦你已经加载了实际时间戳和之前检索到的时间戳,你可以在应用程序中简单地比较它们,而不是在数据库中进行比较。


1
当我将版本列设置为StoreGeneratedPattern = None时,我会收到一个异常,说我不能更新数据库中的时间戳列。 - Colin Desmond
是的,这种行为完全不起作用。我会在晚上尝试一个解决方法并让您知道。 - Ladislav Mrnka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接