PetaPoco - add support for transaction level
This commit is contained in:
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user