9

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

添加任务:在循环中,添加一个新的TestEntity 和一个ReferencingEntity。验证任务:检查是否存在未被任何引用实体引用的 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();
            }
        }
    }
}
4

1 回答 1

7

尝试数据库选项“Is Read Commited Snapshot On”=True。

我们有同样的问题。这个选项解决了他们。

更多信息:

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

在 SQL Server 数据库中原子地添加对象及其关系

于 2013-06-11T07:50:30.090 回答