2013-05-22 18 views
8

DbContext.SaveChanges öğesinin atomik olmadığı bir durumu yeniden üreten aşağıdaki çok basit bir birim sınaması var. Atomik olarak, taahhüt edilen verilerin tüm taahhüt tamamlanmadan önce okunabileceği anlamına gelir.Varlık Çerçeve Kodu İlk: SaveChanges atomik değil

Görev ekle: Bir döngüde yeni bir TestEntity ve ReferencingEntity ekler. Doğrulama görevi: Varlıkları ekleme biçimimden dolayı olması gerekmeyen, herhangi bir ReferencingEntity tarafından başvurulan bir TestEntity olup olmadığını kontrol eder.

Birim testi başarısız ... herhangi bir öneri?

DÜZENLEME: Kabul cevaba göre - InitTest yönteminde eklemek önerilen çözüm ile birim testi çalıştırmak için:

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)); 
} 

Birim testi:

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(); 
      } 
     } 
    } 
} 
+0

Bu kullandığınız veritabanı ve izolasyon seviyesine bağlı olarak bir "kirli okuma" olabilir. Örneğin, SQL Server, bir iş parçacığı tarafından başka bir veriyi okuyabilen bir ileti dizisine izin veren bir yalıtım düzeyine ("READ UNCOMMITTED" (http://msdn.microsoft.com/en-us/library/ms173763(v=sql.100).aspx) sahiptir. işlenmeden önce bir işlemde iş parçacığı. Veriler, ikinci iplik, işlemi geri almaya karar verdiğinde, veritabanından "kaybolabilecekleri" anlamında "kirli" dir. Ancak 'OKUMA UNCOMMITTED' SQL Server'da varsayılan değil. – Slauma

+0

@Slauma Bağlantı havuzu ile SQL Server kullanıyorsa, muhtemelen [önceden ayarlanmış bir yalıtım düzeyini miras alır] (http://support.microsoft.com/kb/972915) bağlantı kuruyor olabilir. @OhadMeir İşlemlerinizi "IsolationLevel.ReadCommitted" yalıtım seviyesinde bir "TransactionScope" ile sarmalamayı ve hatanın devam edip etmediğini görmeyi deneyebilirsiniz. –

+0

IsolationLevel.ReadCommitted eklendi - test hala başarısız –

cevap