public void BeforeCloseAfterCloseCanceled() { var id1 = Guid.NewGuid(); var log = new List <string>(); using (var scope = RhetosProcessHelper.CreateScope()) { var transaction = scope.Resolve <IPersistenceTransaction>(); transaction.BeforeClose += () => log.Add("before"); transaction.AfterClose += () => log.Add("after"); var repository = scope.Resolve <Common.DomRepository>(); repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = TestNamePrefix + Guid.NewGuid() }); } Assert.AreEqual("", TestUtility.Dump(log)); using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); Assert.IsFalse(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); } }
public void RollbackByDefault() { var id1 = Guid.NewGuid(); TestUtility.ShouldFail <FrameworkException>(() => { using (var scope = RhetosProcessHelper.ProcessContainer.CreateTransactionScopeContainer()) { var context = scope.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = TestNamePrefix + Guid.NewGuid() }); throw new FrameworkException(nameof(RollbackByDefault)); // The exception that is not handled within transaction scope. #pragma warning disable CS0162 // Unreachable code detected scope.CommitChanges(); #pragma warning restore CS0162 // Unreachable code detected } }, nameof(RollbackByDefault)); using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); Assert.IsFalse(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); } }
public void IndependentTransactions() { const int threadCount = 2; int initialCount; using (var scope = RhetosProcessHelper.CreateScope()) { RhetosProcessHelper.CheckForParallelism(scope.Resolve <ISqlExecuter>(), threadCount); var context = scope.Resolve <Common.ExecutionContext>(); initialCount = context.Repository.TestEntity.BaseEntity.Query().Count(); } var id1 = Guid.NewGuid(); Parallel.For(0, threadCount, thread => { using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); Assert.AreEqual(initialCount, context.Repository.TestEntity.BaseEntity.Query().Count()); Thread.Sleep(100); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = TestNamePrefix + Guid.NewGuid() }); // Each thread uses the same ID to make sure only one thread can run this code at same time. Assert.AreEqual(initialCount + 1, context.Repository.TestEntity.BaseEntity.Query().Count()); } }); }
public void CommitAndCloseWithDiscard() { var id1 = Guid.NewGuid(); var id2 = Guid.NewGuid(); TestUtility.ShouldFail <FrameworkException>(() => { using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = TestNamePrefix + Guid.NewGuid() }); scope.Resolve <IPersistenceTransaction>().DiscardChanges(); scope.CommitAndClose(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id2, Name = TestNamePrefix + Guid.NewGuid() }); } }, "disposed persistence transaction"); // CommitAndClose should close the transaction, even if it was discarded. using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); // CommitAndClose should rollback the transaction. var ids = new[] { id1, id2 }; Assert.AreEqual( "", TestUtility.DumpSorted(context.Repository.TestEntity.BaseEntity.Load(ids), item => item.ID)); } }
public void EarlyCommitAndClose() { var id1 = Guid.NewGuid(); var id2 = Guid.NewGuid(); TestUtility.ShouldFail <FrameworkException>(() => { using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = TestNamePrefix + Guid.NewGuid() }); scope.CommitAndClose(); // CommitAndClose is incorrectly placed at this position. context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id2, Name = TestNamePrefix + Guid.NewGuid() }); } }, "disposed persistence transaction"); using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); // Only operations before CommitAndClose are committed. var ids = new[] { id1, id2 }; Assert.AreEqual( id1.ToString(), TestUtility.DumpSorted(context.Repository.TestEntity.BaseEntity.Load(ids), item => item.ID)); } }
public void EarlyCommitOnDispose() { var id1 = Guid.NewGuid(); var id2 = Guid.NewGuid(); TestUtility.ShouldFail <FrameworkException>(() => { using (var scope = RhetosProcessHelper.ProcessContainer.CreateTransactionScopeContainer()) { var context = scope.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = TestNamePrefix + Guid.NewGuid() }); scope.CommitChanges(); // CommitChanges is incorrectly placed at this position. context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id2, Name = TestNamePrefix + Guid.NewGuid() }); throw new FrameworkException(nameof(EarlyCommitOnDispose)); // The exception is not handled within transaction scope to discard the transaction. } }, nameof(EarlyCommitOnDispose)); using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); // The transaction is committed because of incorrect implementation pattern above. var ids = new[] { id1, id2 }; Assert.AreEqual( TestUtility.DumpSorted(ids), TestUtility.DumpSorted(context.Repository.TestEntity.BaseEntity.Load(ids), item => item.ID)); } }
public static void ClassCleanup() { using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); var testItems = context.Repository.TestEntity.BaseEntity.Load(item => item.Name.StartsWith(TestNamePrefix)); context.Repository.TestEntity.BaseEntity.Delete(testItems); scope.CommitAndClose(); } }
public void FailedCleanupRollback() { var id1 = Guid.NewGuid(); var log = new List <string>(); var systemLog = new List <string>(); string testName = TestNamePrefix + Guid.NewGuid(); using (var scope = RhetosProcessHelper.CreateScope(builder => builder.AddLogMonitor(systemLog, EventType.Trace))) { var transaction = scope.Resolve <IPersistenceTransaction>(); transaction.BeforeClose += () => log.Add("before1"); transaction.BeforeClose += () => throw new InvalidOperationException(testName + "-before"); transaction.BeforeClose += () => log.Add("before2"); transaction.AfterClose += () => log.Add("after"); var repository = scope.Resolve <Common.DomRepository>(); repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = testName }); var dbTransaction = (DbTransaction)transaction.GetType().GetField("_transaction", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(transaction); dbTransaction.Rollback(); // This will cause error on commit or rollback, when IPersistenceTransaction is Disposed. systemLog.Clear(); TestUtility.ShouldFail <InvalidOperationException>( () => scope.CommitAndClose(), testName + "-before"); TestUtility.AssertContains( string.Join(Environment.NewLine, systemLog), new[] { "Rolling back transaction", "Closing connection" }); TestUtility.ShouldFail <FrameworkException>( () => Assert.IsNull(transaction.Connection), "Trying to use the Connection property of a disposed persistence transaction."); } Assert.AreEqual("before1", TestUtility.Dump(log)); // Failure on rollback should not throw an exception, to allow other cleanup code to be executed. Also, a previously handled database connection error may have triggered the rollback. using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); Assert.IsFalse(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); } }
public void FailedCommit() { var id1 = Guid.NewGuid(); var log = new List <string>(); var systemLog = new List <string>(); string testName = TestNamePrefix + Guid.NewGuid(); using (var scope = RhetosProcessHelper.CreateScope(builder => builder.AddLogMonitor(systemLog, EventType.Trace))) { var transaction = scope.Resolve <IPersistenceTransaction>(); transaction.BeforeClose += () => log.Add("before"); transaction.AfterClose += () => log.Add("after"); var repository = scope.Resolve <Common.DomRepository>(); repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = testName }); var dbTransaction = (DbTransaction)transaction.GetType().GetField("_transaction", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(transaction); dbTransaction.Rollback(); // This will cause error on commit or rollback, when IPersistenceTransaction is Disposed. systemLog.Clear(); TestUtility.ShouldFail( () => scope.CommitAndClose(), "This SqlTransaction has completed; it is no longer usable."); TestUtility.AssertContains( string.Join(Environment.NewLine, systemLog), new[] { "Closing connection" }); TestUtility.ShouldFail <FrameworkException>( () => Assert.IsNull(transaction.Connection), "Trying to use the Connection property of a disposed persistence transaction."); } Assert.AreEqual("before", TestUtility.Dump(log)); using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); Assert.IsFalse(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); } }
public void ExplicitCommitAndClose() { var id1 = Guid.NewGuid(); using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = TestNamePrefix + Guid.NewGuid() }); scope.CommitAndClose(); } using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); Assert.IsTrue(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); } }
public void BeforeCloseFailed() { var id1 = Guid.NewGuid(); var log = new List <string>(); var systemLog = new List <string>(); string testName = TestNamePrefix + Guid.NewGuid(); using (var scope = RhetosProcessHelper.CreateScope(builder => builder.AddLogMonitor(systemLog, EventType.Trace))) { var transaction = scope.Resolve <IPersistenceTransaction>(); transaction.BeforeClose += () => log.Add("before1"); transaction.BeforeClose += () => throw new InvalidOperationException(testName); transaction.BeforeClose += () => log.Add("before2"); transaction.AfterClose += () => log.Add("after"); var repository = scope.Resolve <Common.DomRepository>(); repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1, Name = testName }); TestUtility.ShouldFail <InvalidOperationException>( () => scope.CommitAndClose(), testName); TestUtility.AssertContains( string.Join(Environment.NewLine, systemLog), new[] { "Rolling back transaction", "Closing connection" }); TestUtility.ShouldFail <FrameworkException>( () => Assert.IsNull(transaction.Connection), "Trying to use the Connection property of a disposed persistence transaction."); } Assert.AreEqual("before1", TestUtility.Dump(log)); using (var scope = RhetosProcessHelper.CreateScope()) { var context = scope.Resolve <Common.ExecutionContext>(); Assert.IsFalse(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); } }