Skip to content

Commit

Permalink
Release/add new transactional scope behavior (#6)
Browse files Browse the repository at this point in the history
* add new TransactionalScopeBehavior

* add new TransactionalScopeBehavior
  • Loading branch information
denis-peshkov authored Aug 13, 2024
1 parent 4145ac2 commit d77551b
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Cross.CQRS.EF/Behaviors/TransactionalBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TRe

await executionStrategy.ExecuteAsync(async () =>
{
using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
response = await next();
transaction.Commit();
await transaction.CommitAsync(cancellationToken);
});

// Clean-up tracked entries
Expand Down
61 changes: 61 additions & 0 deletions Cross.CQRS.EF/Behaviors/TransactionalScopeBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace Cross.CQRS.EF.Behaviors;

internal sealed class TransactionalScopeBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : class, IRequest<TResponse>
{
private readonly IDbContextProvider _dbContextProvider;
private readonly IHandlerLocator _handlerLocator;

public TransactionalScopeBehavior(IHandlerLocator handlerLocator, IDbContextProvider dbContextProvider)
{
_handlerLocator = handlerLocator;
_dbContextProvider = dbContextProvider;
}

/// <inheritdoc />
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
if (request is not ICommand<TResponse>)
{
return await next();
}

var handler = _handlerLocator.FindHandlerTypeByRequest(typeof(TRequest));
if (handler != null)
{
var isExplicitTransactionSet = handler
.GetCustomAttributes(typeof(ExplicitTransactionAttribute), inherit: false)
.Any();

if (isExplicitTransactionSet)
{
// Skip behavior if requested explicit transaction management.
return await next();
}
}

var dbContext = _dbContextProvider.Get();
var executionStrategy = dbContext.Database.CreateExecutionStrategy();

TResponse response = default;

await executionStrategy.ExecuteAsync(async () =>
{
using var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
response = await next();
transactionScope.Complete();
});

// Clean-up tracked entries
var trackedEntries = dbContext.ChangeTracker.Entries()
.Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted)
.ToArray();

foreach (var entry in trackedEntries)
{
entry.State = EntityState.Detached;
}

return response;
}
}
5 changes: 5 additions & 0 deletions Cross.CQRS.EF/CqrsRegistrationSyntaxExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ public static CqrsRegistrationSyntax AddEntityFrameworkIntegration<TDbContext>(t
// Behaviors registered earlier will be executed earlier
switch (transactionBehavior)
{
case TransactionBehaviorEnum.NoBehavior:
break;
case TransactionBehaviorEnum.TransactionalBehavior:
syntax.Behaviors.AddBehavior(typeof(TransactionalBehavior<,>), order: 10);
break;
case TransactionBehaviorEnum.ScopeBehavior:
syntax.Behaviors.AddBehavior(typeof(ScopeBehavior<,>), order: 10);
break;
case TransactionBehaviorEnum.TransactionalScopeBehavior:
syntax.Behaviors.AddBehavior(typeof(TransactionalScopeBehavior<,>), order: 10);
break;
default:
throw new ArgumentOutOfRangeException(nameof(transactionBehavior), transactionBehavior, null);
}
Expand Down
5 changes: 5 additions & 0 deletions Cross.CQRS.EF/Enums/TransactionBehaviorEnum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ namespace Cross.CQRS.EF.Enums;

public enum TransactionBehaviorEnum
{
NoBehavior = 0,

TransactionalBehavior = 1,

ScopeBehavior = 2,

TransactionalScopeBehavior = 3,
}

0 comments on commit d77551b

Please sign in to comment.