Entity framework 实体框架代码优先:SaveChanges不是原子的

Entity framework 实体框架代码优先:SaveChanges不是原子的,entity-framework,ef-code-first,atomic,dbcontext,Entity Framework,Ef Code First,Atomic,Dbcontext,我有以下非常简单的单元测试,它再现了DbContext.SaveChanges不是原子的情况。 “非原子”是指在所有提交完成之前可以读取提交的数据 addtask:在循环中,添加新的TestEntity和referencegenty。 验证任务:检查是否存在未被任何ReferenceGenty引用的TestEntity-这是不应该发生的,因为我添加实体的方式 单元测试失败。。。有什么建议吗 编辑: 根据接受的答案-为了使用建议的解决方案运行单元测试,请添加InitTest方法: using (v

我有以下非常简单的单元测试,它再现了DbContext.SaveChanges不是原子的情况。 “非原子”是指在所有提交完成之前可以读取提交的数据

addtask:在循环中,添加新的TestEntity和referencegenty。 验证任务:检查是否存在未被任何ReferenceGenty引用的TestEntity-这是不应该发生的,因为我添加实体的方式

单元测试失败。。。有什么建议吗

编辑: 根据接受的答案-为了使用建议的解决方案运行单元测试,请添加InitTest方法:

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));
}
单元测试:

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();
            }
        }
    }
}
使用System.Data.Entity;
使用System.Linq;
使用System.Threading.Tasks;
使用Microsoft.VisualStudio.TestTools.UnitTesting;
命名空间Atlit.Server.Tests.Integration.SessionProcessing
{
类TestContext:DbContext
{
公共数据库集测试实体{get;set;}
公共数据库集引用属性{get;set;}
}
类测试性
{
public int TestEntityId{get;set;}
}
类引用关系
{
public int referencegentityid{get;set;}
公共TestEntity TestEntity{get;set;}
}
[测试类]
公共类saveChangesTomicTest
{
私有易失性整数m_计数=3000;
私有易失性bool m_Failed=false;
[测试初始化]
公共void InitTest()
{
使用(var context=newtestcontext())
{
var dbInitializer=new DropCreateDatabaseAlways();
dbInitializer.InitializeDatabase(上下文);
}
}
专用无效补遗()
{
而(m_计数-->0&&!m_失败)
{
var transactionOptions=newtransactionoptions{IsolationLevel=IsolationLevel.ReadCommitted};
使用(var transactionScope=new transactionScope(TransactionScopeOption.RequiresNew,transactionOptions))
{
使用(var context=newtestcontext())
{
var entity=context.TestEntities.Add(newtestentity());
Add(新的referencegentity{TestEntity=entity});
SaveChanges();
}
transactionScope.Complete();
}
}        
}
私有void ValidateEntities()
{
而(m_计数>0&!m_失败)
{
if(FreeEntitiesExist())
{
m_Failed=true;
}
}            
}
[测试方法]
公开无效的证明
{
var addTask=Task.Factory.StartNew(AddEntities);
var readTask=Task.Factory.StartNew(ValidateEntities);
addTask.Wait();
readTask.Wait();
IsFalse(freeentiesexist(),“健全失败”);
IsFalse(m_失败,“测试失败”);
}
私有静态bool freeentiesexist()
{
使用(var context=newtestcontext())
{
返回(来自context.TestEntities中的实体)
其中!context.referencegenties.Any(re=>re.TestEntity.TestEntityId==entity.TestEntityId)
选择实体)
.ToArray().Any();
}
}
}
}
尝试数据库选项“Is Read committed Snapshot On”=True

我们也有同样的问题。这一选择解决了这些问题

更多关于:


这可能是“脏读”,具体取决于您使用的数据库和隔离级别。例如,SQL Server有一个隔离级别
readuncommitted
(),允许线程在事务提交之前读取另一个线程插入的数据。数据是“脏”的,因为当第二个线程决定回滚事务时,它们可能会从数据库中“消失”。但是,SQL Server中的默认设置不是
READ UNCOMMITTED
。@Slauma如果他使用的是带有连接池的SQL Server,他可能会得到一个连接@OhadMeir您可以尝试使用显式设置的隔离级别
IsolationLevel.ReadCommitted
将操作包装在
TransactionScope
中,并查看错误是否继续。添加的IsolationLevel.ReadCommitted-测试仍然失败此处的名称问题;关于这个问题有什么消息吗?还添加了一个答案,说明如何在单元测试中解决这个问题