PetaPoco - add support for transaction level

This commit is contained in:
Stephan
2015-08-25 15:40:33 +02:00
parent 5cfb4f5122
commit a6a6edb9f2

View File

@@ -215,6 +215,10 @@ namespace Umbraco.Core.Persistence
_paramPrefix = "?";
if (_dbType == DBType.Oracle)
_paramPrefix = ":";
// by default use MSSQL default ReadCommitted level
//TODO change to RepeatableRead - but that is breaking
_isolationLevel = IsolationLevel.ReadCommitted;
}
// Automatically close one open shared connection
@@ -237,7 +241,27 @@ namespace Umbraco.Core.Persistence
_sharedConnection.ConnectionString = _connectionString;
_sharedConnection.OpenWithRetry();//Changed .Open() => .OpenWithRetry() extension method
_sharedConnection = OnConnectionOpened(_sharedConnection);
// ensure we have the proper isolation level, as levels can leak in pools
// read http://stackoverflow.com/questions/9851415/sql-server-isolation-level-leaks-across-pooled-connections
// and http://stackoverflow.com/questions/641120/what-exec-sp-reset-connection-shown-in-sql-profiler-means
//
// NPoco has that code in OpenSharedConnectionImp (commented out?)
//using (var cmd = _sharedConnection.CreateCommand())
//{
// cmd.CommandText = GetSqlForTransactionLevel(_isolationLevel);
// cmd.CommandTimeout = CommandTimeout;
// cmd.ExecuteNonQuery();
//}
//
// although MSDN documentation for SQL CE clearly states that the above method
// should work, it fails & reports an error parsing the query on 'TRANSACTION',
// and Google is no help (others have faced the same issue... no solution). So,
// switching to another method that does work on all databases.
var tr = _sharedConnection.BeginTransaction(_isolationLevel);
tr.Commit();
tr.Dispose();
_sharedConnection = OnConnectionOpened(_sharedConnection);
if (KeepConnectionAlive)
_sharedConnectionDepth++; // Make sure you call Dispose
@@ -269,10 +293,20 @@ namespace Umbraco.Core.Persistence
// Helper to create a transaction scope
public Transaction GetTransaction()
{
return new Transaction(this);
}
return GetTransaction(_isolationLevel);
}
// Use by derived repo generated by T4 templates
public Transaction GetTransaction(IsolationLevel isolationLevel)
{
return new Transaction(this, isolationLevel);
}
public IsolationLevel CurrentTransactionIsolationLevel
{
get { return _transaction == null ? IsolationLevel.Unspecified : _transaction.IsolationLevel; }
}
// Use by derived repo generated by T4 templates
public virtual void OnBeginTransaction() { }
public virtual void OnEndTransaction() { }
@@ -281,17 +315,23 @@ namespace Umbraco.Core.Persistence
// Use `using (var scope=db.Transaction) { scope.Complete(); }` to ensure correct semantics
public void BeginTransaction()
{
_transactionDepth++;
BeginTransaction(_isolationLevel);
}
public void BeginTransaction(IsolationLevel isolationLevel)
{
_transactionDepth++;
if (_transactionDepth == 1)
{
OpenSharedConnection();
_transaction = _sharedConnection.BeginTransaction();
_transaction = _sharedConnection.BeginTransaction(isolationLevel);
_transactionCancelled = false;
OnBeginTransaction();
}
}
else if (isolationLevel > _transaction.IsolationLevel)
throw new Exception("Already in a transaction with a lower isolation level.");
}
// Internal helper to cleanup transaction stuff
void CleanupTransaction()
@@ -324,7 +364,27 @@ namespace Umbraco.Core.Persistence
CleanupTransaction();
}
// Helper to handle named parameters from object properties
// in NPoco this is in DatabaseType
private static string GetSqlForTransactionLevel(IsolationLevel isolationLevel)
{
switch (isolationLevel)
{
case IsolationLevel.ReadCommitted:
return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
case IsolationLevel.ReadUncommitted:
return "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
case IsolationLevel.RepeatableRead:
return "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ";
case IsolationLevel.Serializable:
return "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE";
case IsolationLevel.Snapshot:
return "SET TRANSACTION ISOLATION LEVEL SNAPSHOT";
default:
return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
}
}
// Helper to handle named parameters from object properties
static Regex rxParams = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
public static string ProcessParams(string _sql, object[] args_src, List<object> args_dest)
{
@@ -2264,16 +2324,17 @@ namespace Umbraco.Core.Persistence
string _lastSql;
object[] _lastArgs;
string _paramPrefix = "@";
IsolationLevel _isolationLevel;
}
// Transaction object helps maintain transaction depth counts
public class Transaction : IDisposable
{
public Transaction(Database db)
{
_db = db;
_db.BeginTransaction();
}
public Transaction(Database db, IsolationLevel isolationLevel)
{
_db = db;
_db.BeginTransaction(isolationLevel);
}
public virtual void Complete()
{