Entity framework 实体框架代码优先:SaveChanges不是原子的
我有以下非常简单的单元测试,它再现了DbContext.SaveChanges不是原子的情况。 “非原子”是指在所有提交完成之前可以读取提交的数据 addtask:在循环中,添加新的TestEntity和referencegenty。 验证任务:检查是否存在未被任何ReferenceGenty引用的TestEntity-这是不应该发生的,因为我添加实体的方式 单元测试失败。。。有什么建议吗 编辑: 根据接受的答案-为了使用建议的解决方案运行单元测试,请添加InitTest方法: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
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-测试仍然失败此处的名称问题;关于这个问题有什么消息吗?还添加了一个答案,说明如何在单元测试中解决这个问题