context: using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Linq;
  using System.Threading;
  using System.Transactions;
  using NHibernate.Cfg;
  using NHibernate.Driver;
  using NHibernate.Engine;
  using NHibernate.Test.TransactionTest;
  using NUnit.Framework;
  
  namespace NHibernate.Test.SystemTransactions
  {
  	[TestFixture]
  	public class SystemTransactionFixture : SystemTransactionFixtureBase
  	{
  		protected override bool UseConnectionOnSystemTransactionPrepare => true;
  		protected override bool AutoJoinTransaction => true;
  
  		[Test]
  		public void WillNotCrashOnPrepareFailure()
  		{
  			IgnoreIfUnsupported(false);
  			var tx = new TransactionScope();
  			var disposeCalled = false;
  			try
  			{
  				using (var s = OpenSession())
  				{
  					s.Save(new Person { NotNullData = null }); // Cause a SQL not null constraint violation.
  				}
  
  				tx.Complete();
  				disposeCalled = true;
  				Assert.Throws<TransactionAbortedException>(tx.Dispose, "Scope disposal has not rollback and throw.");
  			}
  			finally
  			{
  				if (!disposeCalled)
  				{
  					try
  					{
  						tx.Dispose();
  					}
  					catch
  					{
  						// Ignore, if disposed has not been called, another exception has occurred in the try and
  						// we should avoid overriding it by the disposal failure.
  					}
  				}
  			}
  		}
  
  		[Theory]
  		public void CanRollbackTransactionFromScope(bool explicitFlush)
  		{
  			IgnoreIfUnsupported(explicitFlush);
  			using (new TransactionScope())
  			using (var s = OpenSession())
  			{
  				s.Save(new Person());
  
  				if (explicitFlush)
  					s.Flush();
  				// No Complete call for triggering rollback.
  			}
  
  			AssertNoPersons();
  		}
  
  		[Theory]
  		[Description("rollback inside nh-session-scope should not commit save and the transaction should be aborted.")]
  		public void TransactionInsertWithRollBackFromScope(bool explicitFlush)
  		{
  			IgnoreIfUnsupported(explicitFlush);
  			using (new TransactionScope())
  			{
  				using (var s = OpenSession())
  				{
  					var person = new Person();
  					s.Save(person);
  
  					if (explicitFlush)
  						s.Flush();
  				}
  				// No Complete call for triggering rollback.
  			}
  			AssertNoPersons();
  		}
  
  		[Theory]
  		[Description(@"Two session in two txscope
   (without an explicit NH transaction)
   and with a rollback in the second and a rollback outside nh-session-scope.")]
  		public void TransactionInsertLoadWithRollBackFromScope(bool explicitFlush)
  		{
  			IgnoreIfUnsupported(explicitFlush);
  			object savedId;
  			var createdAt = DateTime.Today;
  			using (var txscope = new TransactionScope())
  			{
  				using (var s = OpenSession())
  				{
  					var person = new Person { CreatedAt = createdAt };
  					savedId = s.Save(person);
  
  					if (explicitFlush)
  						s.Flush();
  				}
  				txscope.Complete();
  			}
  
  			using (new TransactionScope())
  			{
  				using (var s = OpenSession())
  				{
  					var person = s.Get<Person>(savedId);
  					person.CreatedAt = createdAt.AddMonths(-1);
  
  					if (explicitFlush)
  						s.Flush();
  				}
  
  				// No Complete call for triggering rollback.
  			}
  
  			using (var s = OpenSession())
  			using (s.BeginTransaction())
  			{
  				Assert.AreEqual(createdAt, s.Get<Person>(savedId).CreatedAt, "Entity update was not rollback-ed.");
  			}
  		}
  
  		[Theory]
  		public void CanDeleteItem(bool explicitFlush)
  		{
  			IgnoreIfUnsupported(explicitFlush);
  			object id;
  			using (var tx = new TransactionScope())
  			{
  				using (var s = OpenSession())
  				{
  					id = s.Save(new Person());
  
  					if (explicitFlush)
  						s.Flush();
  
  					tx.Complete();
  				}
  			}
  
  			using (var s = OpenSession())
  			using (s.BeginTransaction())
  			{
  				Assert.AreEqual(1, s.Query<Person>().Count(), "Entity not found in database.");
  			}
  
  			using (var tx = new TransactionScope())
  			{
  				using (var s = OpenSession())
  				{
  					s.Delete(s.Get<Person>(id));
  
  					if (explicitFlush)
  						s.Flush();
  
  					tx.Complete();
  				}
  			}
  
  			AssertNoPersons();
  		}
  
  		[Theory]
  		public void CanUseSessionWithManyScopes(bool explicitFlush)
  		{
  			IgnoreIfUnsupported(explicitFlush);
  			// ODBC with SQL-Server always causes scopes to go distributed, which causes their transaction completion to run
  			// asynchronously. But ODBC enlistment also check the previous transaction in a way that do not guard against it
  			// being concurrently disposed of. See https://github.com/nhibernate/nhibernate-core/pull/1505 for more details.
  			if (Sfi.ConnectionProvider.Driver is OdbcDriver)
  				Assert.Ignore("ODBC sometimes fails on second scope by checking the previous transaction status, which may yield an object disposed exception");
  			// SAP HANA & SQL Anywhere .Net providers always cause system transactions to be distributed, causing them to
  			// complete on concurrent threads. This creates race conditions when chaining scopes, the subsequent scope usage
  			// finding the connection still enlisted in the previous transaction, its complete being still not finished
  			// on its own thread.
  			if (Sfi.ConnectionProvider.Driver is HanaDriverBase || Sfi.ConnectionProvider.Driver is SapSQLAnywhere17Driver)
  				Assert.Ignore("SAP HANA and SQL Anywhere scope handling causes concurrency issues preventing chaining scope usages.");
  
  			using (var s = WithOptions().ConnectionReleaseMode(ConnectionReleaseMode.OnClose).OpenSession())
  			{
  				using (var tx = new TransactionScope())
  				{
  					if (!AutoJoinTransaction)
  						s.JoinTransaction();
  					// Acquire the connection
  					var count = s.Query<Person>().Count();
  					Assert.That(count, Is.EqualTo(0), "Unexpected initial entity count.");
  					tx.Complete();
  				}
  
  				using (var tx = new TransactionScope())
  				{
  					if (!AutoJoinTransaction)
  						s.JoinTransaction();
  					s.Save(new Person());
  
  					if (explicitFlush)
  						s.Flush();
  
  					tx.Complete();
  				}
  
  				using (var tx = new TransactionScope())
  				{
  					if (!AutoJoinTransaction)
  						s.JoinTransaction();
  
gt: 					var count = s.Query<Person>().Count();