Skip to content

Commit

Permalink
UoW: Decorators for Connection and Transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
marcwittke committed Dec 21, 2019
1 parent 1741fa0 commit b563b4b
Show file tree
Hide file tree
Showing 23 changed files with 429 additions and 467 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public IDisposable BeginScope(IIdentity identity = null, TenantId tenantId = nul

return new MultipleDisposable(scope, scopeDurationLogger);
}

public void Run<TJob>() where TJob : class, IJob
{
var tenantIds = TenantIdService.GetActiveTenantIds();
Expand All @@ -91,23 +91,21 @@ public void Invoke(Action action, IIdentity identity, TenantId tenantId)
{
using (BeginScope(new SystemIdentity(), tenantId))
{
using (var unitOfWork = CompositionRoot.GetInstance<IUnitOfWork>())
var unitOfWork = CompositionRoot.GetInstance<IUnitOfWork>();
try
{
unitOfWork.Begin();
action.Invoke();
unitOfWork.Complete();
}
catch (TargetInvocationException ex)
{
ExceptionLogger.LogException(ex.InnerException ?? ex);
}
catch (Exception ex)
{
try
{
unitOfWork.Begin();
action.Invoke();
unitOfWork.Complete();
}
catch (TargetInvocationException ex)
{
ExceptionLogger.LogException(ex.InnerException ?? ex);
}
catch (Exception ex)
{
Logger.Info(ex);
ExceptionLogger.LogException(ex);
}
Logger.Info(ex);
ExceptionLogger.LogException(ex);
}
}
}
Expand All @@ -116,23 +114,21 @@ public async Task InvokeAsync(Func<Task> awaitableAsyncAction, IIdentity identit
{
using (BeginScope(new SystemIdentity(), tenantId))
{
using (var unitOfWork = CompositionRoot.GetInstance<IUnitOfWork>())
var unitOfWork = CompositionRoot.GetInstance<IUnitOfWork>();
try
{
unitOfWork.Begin();
await awaitableAsyncAction.Invoke();
unitOfWork.Complete();
}
catch (TargetInvocationException ex)
{
ExceptionLogger.LogException(ex.InnerException ?? ex);
}
catch (Exception ex)
{
try
{
unitOfWork.Begin();
await awaitableAsyncAction.Invoke();
unitOfWork.Complete();
}
catch (TargetInvocationException ex)
{
ExceptionLogger.LogException(ex.InnerException ?? ex);
}
catch (Exception ex)
{
Logger.Info(ex);
ExceptionLogger.LogException(ex);
}
Logger.Info(ex);
ExceptionLogger.LogException(ex);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Data;

namespace Backend.Fx.Patterns.Transactions
{
public class ReadonlyDecorator : ITransactionContext
{
private readonly ITransactionContext _transactionContext;

public ReadonlyDecorator(ITransactionContext transactionContext)
{
_transactionContext = transactionContext;
}

public void BeginTransaction() => _transactionContext.BeginTransaction();

public void CommitTransaction()
{
RollbackTransaction();
}

public void RollbackTransaction() => _transactionContext.RollbackTransaction();


public IDbTransaction CurrentTransaction => _transactionContext.CurrentTransaction;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,64 @@
using System.Data;
using Backend.Fx.Logging;

namespace Backend.Fx.Patterns.UnitOfWork
namespace Backend.Fx.Patterns.Transactions
{
public class TransactionContext : IDisposable
/// <summary>
/// wraps an underlying database transaction. In combination with a injection container, access to
/// the current transaction can be gained by means of this interface.
/// </summary>
public interface ITransactionContext
{
IDbTransaction CurrentTransaction { get; }
void BeginTransaction();
void CommitTransaction();
void RollbackTransaction();
}

public class TransactionContext : ITransactionContext, IDisposable
{
private static readonly ILogger Logger = LogManager.Create<TransactionContext>();
private IDisposable _transactionLifetimeLogger;
private readonly bool _shouldHandleConnectionState;

public IDbConnection Connection { get; }

public IDbTransaction CurrentTransaction { get; private set; }

private IDisposable _transactionLifetimeLogger;

public TransactionContext(IDbConnection connection)
{
Connection = connection;
ConnectionState connectionState = Connection.State;
switch (connectionState)
ConnectionState state = Connection.State;
switch (state)
{
case ConnectionState.Open:
_shouldHandleConnectionState = false;
break;
case ConnectionState.Closed:
_shouldHandleConnectionState = true;
break;
case ConnectionState.Open:
_shouldHandleConnectionState = false;
break;
default:
throw new InvalidOperationException($"A connection provided to the TransactionContext must either be closed or open, but must not be {connectionState}");
throw new InvalidOperationException($"A connection provided to the TransactionContext must either be closed or open, but must not be {state}");
}
}

public IDbConnection Connection { get; }

public IDbTransaction CurrentTransaction { get; private set; }

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

public void BeginTransaction()
{
if (_shouldHandleConnectionState)
{
Connection.Open();
}

CurrentTransaction = Connection.BeginTransaction();
_transactionLifetimeLogger = Logger.DebugDuration("Transaction open");
}

public void CommitTransaction()
{
CurrentTransaction.Commit();
Expand All @@ -54,6 +73,19 @@ public void CommitTransaction()
}
}

public void RollbackTransaction()
{
CurrentTransaction.Rollback();
CurrentTransaction.Dispose();
CurrentTransaction = null;
_transactionLifetimeLogger?.Dispose();
_transactionLifetimeLogger = null;
if (_shouldHandleConnectionState)
{
Connection.Close();
}
}

private void Dispose(bool disposing)
{
if (disposing)
Expand All @@ -71,16 +103,10 @@ private void Dispose(bool disposing)
{
Logger.Error(ex, "Dispose failed");
}

_transactionLifetimeLogger?.Dispose();
CurrentTransaction?.Dispose();
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Data;
using System.Security.Principal;
using Backend.Fx.Patterns.DependencyInjection;

namespace Backend.Fx.Patterns.UnitOfWork
{
/// <summary>
/// Enriches the unit of work to open and close a database connection during lifetime
/// </summary>
public class DbConnectionDecorator : IUnitOfWork
{
public DbConnectionDecorator(IDbConnection dbConnection, IUnitOfWork unitOfWork)
{
DbConnection = dbConnection;
UnitOfWork = unitOfWork;
}

public IUnitOfWork UnitOfWork { get; }

public IDbConnection DbConnection { get; }

public ICurrentTHolder<IIdentity> IdentityHolder => UnitOfWork.IdentityHolder;

public void Begin()
{
DbConnection.Open();
UnitOfWork.Begin();
}

public void Complete()
{
UnitOfWork.Complete();
DbConnection.Close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Security.Principal;
using Backend.Fx.Patterns.DependencyInjection;
using Backend.Fx.Patterns.Transactions;

namespace Backend.Fx.Patterns.UnitOfWork
{
/// <summary>
/// Enriches the unit of work to use a database transaction during lifetime.
/// </summary>
public class DbTransactionDecorator : IUnitOfWork
{
public DbTransactionDecorator(ITransactionContext transactionContext, IUnitOfWork unitOfWork)
{
TransactionContext = transactionContext;
UnitOfWork = unitOfWork;
}

public IUnitOfWork UnitOfWork { get; }

public ITransactionContext TransactionContext { get; }

public ICurrentTHolder<IIdentity> IdentityHolder => UnitOfWork.IdentityHolder;

public void Begin()
{
TransactionContext.BeginTransaction();
UnitOfWork.Begin();
}

public void Complete()
{
UnitOfWork.Complete();
TransactionContext.CommitTransaction();
}
}
}
83 changes: 0 additions & 83 deletions src/abstractions/Backend.Fx/Patterns/UnitOfWork/DbUnitOfWork.cs

This file was deleted.

Loading

0 comments on commit b563b4b

Please sign in to comment.