Entity Framework Code First: SaveChanges is not atomic

I have the following very simple unit test, which reproduces the case when DbContext.SaveChanges is not atomic. By non-atomic, I mean that the captured data can be read before the commit is completed.

Add task: a new TestEntity and ReferencingEntity are added to the loop. Task Verification: Checks if there is a TestEntity that no ReferenceEntity refers to - this should not be due to the way I add entities.

unit test fails ... any advice?

EDIT: According to the accepted answer - to start the unit test with the proposed solution, add 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(); } } } } 
+8
atomic entity-framework dbcontext ef-code-first
source share
1 answer

Try the database option "Read enabled with snapshot enabled" = True.

We had the same problems. This option enabled them.

Additional information on:

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

and

Animate an object and its relationships in a SQL Server database

+6
source share

All Articles