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

时间:2011-03-16 15:32:32

标签: asp.net-mvc-3 entity-framework-4 optimistic-concurrency

我们有一个使用EF4作为其数据访问层的ASP.Net MVC应用程序,我们发现当我们认为它们应该是没有抛出OptimisitcConcurrencyExceptions时的意外行为。

我们已将问题简化为以下代码......

   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应用程序中发生的相当简单的版本,但是会出现同样的问题。

当我们在第三个实体上调用SaveChanges时,我期待异常并没有抛出任何东西。

更有趣的是,当我们附加SQL Profiler时,我们看到版本正在where子句中使用,但它是使用的第三个实体版本值(数据库中的当前版本),而不是第一个实体值DESPITE它是在调用SaveChanges之前立即显式设置。 SaveChanges正在将版本重置为检索的值而不是设置值。

在EDMX中,Version设置为StoreGeneratedPattern设置为Computed。

任何人都知道这里发生了什么?

1 个答案:

答案 0 :(得分: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:

顺便说一下。一旦你同时加载了实际加载的时间戳和之前检索的时间戳,你只需compare them in your application而不是在数据库中进行。