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 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 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 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)); } }
private Exception[] ExecuteParallel(Action <Common.ExecutionContext>[] actions, Action <Common.ExecutionContext> coldStartInsert, Action <Common.ExecutionContext> coldStartQuery) { int threadCount = actions.Count(); using (var container = new RhetosTestContainer(true)) { var sqlExecuter = container.Resolve <ISqlExecuter>(); RhetosProcessHelper.CheckForParallelism(sqlExecuter, threadCount); DeleteOldData(container); coldStartInsert(container.Resolve <Common.ExecutionContext>()); } var containers = Enumerable.Range(0, threadCount).Select(t => new RhetosTestContainer(true)).ToArray(); var exceptions = new Exception[threadCount]; try { var contexts = containers.Select(c => c.Resolve <Common.ExecutionContext>()).ToArray(); foreach (var context in contexts) { coldStartQuery(context); } Parallel.For(0, threadCount, thread => { try { actions[thread].Invoke(contexts[thread]); } catch (Exception ex) { exceptions[thread] = ex; contexts[thread].PersistenceTransaction.DiscardChanges(); } finally { containers[thread].Dispose(); containers[thread] = null; } }); } finally { foreach (var c in containers) { if (c != null) { c.Dispose(); } } } for (int x = 0; x < threadCount; x++) { Console.WriteLine("Exception " + x + ": " + exceptions[x] + "."); } return(exceptions); }
public void EarlyCommit() { var id1 = Guid.NewGuid(); try { using (var container = RhetosProcessHelper.CreateTransactionScopeContainer()) { container.CommitChanges(); // CommitChanges is incorrectly places at this position. var context = container.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1 }); throw new FrameworkException(nameof(EarlyCommit)); // The exception is not handled within transaction scope to discard the transaction. } } catch (FrameworkException ex) { Console.WriteLine($"{ex.GetType().Name}: {ex.Message}"); } using (var container = RhetosProcessHelper.CreateTransactionScopeContainer()) { var context = container.Resolve <Common.ExecutionContext>(); Assert.IsTrue(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); // The transaction is committed because of incorrect implementation pattern above. } }
public void RollbackByDefault() { var id1 = Guid.NewGuid(); try { using (var container = RhetosProcessHelper.CreateTransactionScopeContainer()) { var context = container.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1 }); throw new FrameworkException(nameof(RollbackByDefault)); // The exception that is not handled within transaction scope. #pragma warning disable CS0162 // Unreachable code detected container.CommitChanges(); #pragma warning restore CS0162 // Unreachable code detected } } catch (FrameworkException ex) { Console.WriteLine($"{ex.GetType().Name}: {ex.Message}"); } using (var container = RhetosProcessHelper.CreateTransactionScopeContainer()) { var context = container.Resolve <Common.ExecutionContext>(); Assert.IsFalse(context.Repository.TestEntity.BaseEntity.Query(new[] { id1 }).Any()); } }
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 ExplicitCommit() { var id1 = Guid.NewGuid(); using (var container = RhetosProcessHelper.CreateTransactionScopeContainer()) { var context = container.Resolve <Common.ExecutionContext>(); context.Repository.TestEntity.BaseEntity.Insert(new TestEntity.BaseEntity { ID = id1 }); container.CommitChanges(); } using (var container = RhetosProcessHelper.CreateTransactionScopeContainer()) { var context = container.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()); } }
private void Execute2ParallelInserts(int testCount, Action <int, TestAutoCodeCached._Helper.Simple_Repository> action) { const int threadCount = 2; using (var container = new RhetosTestContainer(true)) { DeleteOldData(container); RhetosProcessHelper.CheckForParallelism(container.Resolve <ISqlExecuter>(), threadCount); } for (int test = 1; test <= testCount; test++) { Console.WriteLine("Test: " + test); var containers = new[] { new RhetosTestContainer(true), new RhetosTestContainer(true) }; var repositories = containers.Select(c => c.Resolve <Common.DomRepository>().TestAutoCodeCached.Simple).ToList(); foreach (var r in repositories) { Assert.IsTrue(r.Query().Count() >= 0); // Cold start. } try { Parallel.For(0, threadCount, process => { action(process, repositories[process]); containers[process].Dispose(); containers[process] = null; }); } finally { foreach (var c in containers) { if (c != null) { c.Dispose(); } } } } }