实体框架代码首先:SaveChanges不是原子的 [英] Entity Framework Code First: SaveChanges is not atomic

查看:181
本文介绍了实体框架代码首先:SaveChanges不是原子的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下非常简单的单元测试来重现DbContext.SaveChanges不是原子的情况。
不是原子我的意思是在完成提交之前可以读取提交的数据。



添加任务:在循环中,添加一个新的TestEntity和引用实例。
验证任务:检查是否有没有任何ReferencingEntity引用的TestEntity - 这不是因为添加实体的方式而发生。



单位测试失败...任何建议?



编辑:
根据接受的答案 - 为了运行单元测试与提出的解决方案添加在InitTest方法中:

  using(var context = new TestContext())
{
var objectContext =(上下文为IObjectContextAdapter).ObjectContext;
objectContext.ExecuteStoreCommand(string.Format(ALTER DATABASE [{0}] SET READ_COMMITTED_SNAPSHOT ON,context.GetType()。FullName));
}

单元测试:

  using System.Data.Entity; 
使用System.Linq;
使用System.Threading.Tasks;
使用Microsoft.VisualStudio.TestTools.UnitTesting;

命名空间Atlit.Server.Tests.Integration.SessionProcessing
{
class TestContext:DbContext
{
public DbSet&Test; TestEntity> TestEntities {get;组; }
public DbSet< ReferencingEntity>参考实体{get;组; }
}

class TestEntity
{
public int TestEntityId {get;组; }
}

class ReferencingEntity
{
public int ReferencingEntityId {get;组; }
public TestEntity TestEntity {get;组;
}

[TestClass]
public class SaveChangesAtomicTest
{
private volatile int m_Count = 3000;
private volatile bool m_Failed = false;

[TestInitialize]
public void InitTest()
{
using(var context = new TestContext())
{
var dbInitializer = new DropCreateDatabaseAlways< TestContext>();
dbInitializer.InitializeDatabase(context);
}
}

private void AddEntities()
{
while(m_Count--> 0&&!m_Failed)
{
var transactionOptions = new TransactionOptions {IsolationLevel = IsolationLevel.ReadCommitted};
using(var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew,transactionOptions))
{
using(var context = new TestContext())
{
var entity = context.TestEntities.Add(new TestEntity());
context.ReferencingEntities.Add(new ReferencingEntity {TestEntity = entity});
context.SaveChanges();
}
transactionScope.Complete();
}
}
}

private void ValidateEntities()
{
while(m_Count> 0&&!m_Failed)
{
if(FreeEntitiesExist())
{
m_Failed = true;
}
}
}

[TestMethod]
public void TestIsSaveChangesAtomic()
{
var addTask = Task.Factory .StartNew(AddEntities);
var readTask = Task.Factory.StartNew(ValidateEntities);

addTask.Wait();
readTask.Wait();

Assert.IsFalse(FreeEntitiesExist(),sanity failed);
Assert.IsFalse(m_Failed,test failed);
}

private static bool FreeEntitiesExist()
{
using(var context = new TestContext())
{
return实体在上下文中.TestEntities
其中!context.ReferencingEntities.Any(re => re.TestEntity.TestEntityId == entity.TestEntityId)
select entity)
.ToArray()。 );
}
}
}
}


解决方案

尝试数据库选项Read Read快照开启= =



我们有同样的问题。



http://msdn.microsoft.com/en-us/library/ms173763.aspx





在SQL Server数据库中以原子方式添加对象及其关系


I have the following very simple unit test that reproduces a case where DbContext.SaveChanges is not atomic. By not atomic I mean that the committed data can be read before all the commit was completed.

Add task: In a loop, adds a new TestEntity and a ReferencingEntity. Validate task: checks if there is a TestEntity that is not referenced by any ReferencingEntity - that is not supposed to happen because of the way I add the entities.

The unit test fails... any advice?

EDIT: According to the accepted answer - In order to run the unit test with the proposed solution add in the InitTest method:

using (var context = new TestContext())
{
    var objectContext = (context as IObjectContextAdapter).ObjectContext;
    objectContext.ExecuteStoreCommand(string.Format("ALTER DATABASE [{0}] SET READ_COMMITTED_SNAPSHOT ON", context.GetType().FullName));
}

Unit test:

using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Atlit.Server.Tests.Integration.SessionProcessing
{
    class TestContext : DbContext
    {
        public DbSet<TestEntity> TestEntities { get; set; }
        public DbSet<ReferencingEntity> ReferencingEntities { get; set; }
    }

    class TestEntity
    {
        public int TestEntityId { get; set; }
    }

    class ReferencingEntity
    {
        public int ReferencingEntityId { get; set; }
        public TestEntity TestEntity { get; set; } 
    }

    [TestClass]
    public class SaveChangesAtomicTest
    {
        private volatile int m_Count = 3000;
        private volatile bool m_Failed = false;

        [TestInitialize]
        public void InitTest()
        {
            using (var context = new TestContext())
            {
                var dbInitializer = new DropCreateDatabaseAlways<TestContext>();
                dbInitializer.InitializeDatabase(context);
            }
        }

        private void AddEntities()
        {
            while (m_Count-- > 0 && !m_Failed)
            {
                var transactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
                using (var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, transactionOptions))
                {
                    using (var context = new TestContext())
                    {
                        var entity = context.TestEntities.Add(new TestEntity());
                        context.ReferencingEntities.Add(new ReferencingEntity { TestEntity = entity });
                        context.SaveChanges();
                    }
                    transactionScope.Complete();
                }
            }        
        }

        private void ValidateEntities()
        {
            while (m_Count > 0 && !m_Failed)
            {
                if (FreeEntitiesExist())
                {
                    m_Failed = true;
                }
            }            
        }

        [TestMethod]
        public void TestIsSaveChangesAtomic()
        {
            var addTask = Task.Factory.StartNew(AddEntities);
            var readTask = Task.Factory.StartNew(ValidateEntities);

            addTask.Wait();
            readTask.Wait();

            Assert.IsFalse(FreeEntitiesExist(), "sanity failed");
            Assert.IsFalse(m_Failed, "test failed");
        }

        private static bool FreeEntitiesExist()
        {
            using (var context = new TestContext())
            {
                return (from entity in context.TestEntities
                        where !context.ReferencingEntities.Any(re => re.TestEntity.TestEntityId == entity.TestEntityId)
                        select entity)
                        .ToArray().Any();
            }
        }
    }
}

解决方案

Try the database option "Is Read Commited Snapshot On"=True.

We had same kind of issues. This option solved them.

More information on:

http://msdn.microsoft.com/en-us/library/ms173763.aspx

and

Add object and its relationships atomically in SQL Server database

这篇关于实体框架代码首先:SaveChanges不是原子的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆