实体框架代码首先:SaveChanges不是原子的 [英] Entity Framework Code First: SaveChanges is not atomic
问题描述
我有以下非常简单的单元测试来重现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
和
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屋!