From 275a64380bf340da9795ea7296c9a8c6f1d63378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 26 Sep 2017 14:39:33 +0300 Subject: [PATCH] Added unit tests. --- .../EfCoreTransactionApi.cs | 14 +++- src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs | 20 +++-- .../Volo/Abp/Uow/ISupportsRollback.cs | 12 +++ src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs | 8 +- src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs | 73 +++++++++++++++++-- .../Volo/Abp/Uow/UnitOfWorkEventArgs.cs | 20 +++++ .../Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs | 12 ++- .../EntityFrameworkCore/Transaction_Tests.cs | 21 +++++- .../Volo/Abp/Uow/UnitOfWork_Events_Tests.cs | 31 ++++++++ 9 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 src/Volo.Abp/Volo/Abp/Uow/ISupportsRollback.cs create mode 100644 src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkEventArgs.cs diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreTransactionApi.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreTransactionApi.cs index f5016f7bda..a715d8c150 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreTransactionApi.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/EfCoreTransactionApi.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; @@ -6,7 +7,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.Uow.EntityFrameworkCore { - public class EfCoreTransactionApi : ITransactionApi + public class EfCoreTransactionApi : ITransactionApi, ISupportsRollback { public IDbContextTransaction DbContextTransaction { get; } public DbContext StarterDbContext { get; } @@ -44,5 +45,16 @@ namespace Volo.Abp.Uow.EntityFrameworkCore { DbContextTransaction.Dispose(); } + + public void Rollback() + { + DbContextTransaction.Rollback(); + } + + public Task RollbackAsync(CancellationToken cancellationToken) + { + DbContextTransaction.Rollback(); + return Task.CompletedTask; + } } } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs index b476bc5996..4fc789c6ae 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs @@ -17,9 +17,9 @@ namespace Volo.Abp.Uow public string ReservationName => _parent.ReservationName; - public event EventHandler Completed; + public event EventHandler Completed; public event EventHandler Failed; - public event EventHandler Disposed; + public event EventHandler Disposed; public IServiceProvider ServiceProvider => _parent.ServiceProvider; @@ -60,17 +60,27 @@ namespace Volo.Abp.Uow { return _parent.SaveChangesAsync(cancellationToken); } - + public void Complete() { - + } public Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken)) { return Task.CompletedTask; } - + + public void Rollback() + { + _parent.Rollback(); + } + + public Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + return _parent.RollbackAsync(cancellationToken); + } + public IDatabaseApi FindDatabaseApi(string key) { return _parent.FindDatabaseApi(key); diff --git a/src/Volo.Abp/Volo/Abp/Uow/ISupportsRollback.cs b/src/Volo.Abp/Volo/Abp/Uow/ISupportsRollback.cs new file mode 100644 index 0000000000..4c211d652d --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Uow/ISupportsRollback.cs @@ -0,0 +1,12 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.Uow +{ + public interface ISupportsRollback + { + void Rollback(); + + Task RollbackAsync(CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs index f3436023c9..79089cd8fc 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs @@ -9,11 +9,11 @@ namespace Volo.Abp.Uow { Guid Id { get; } - event EventHandler Completed; + event EventHandler Completed; event EventHandler Failed; - event EventHandler Disposed; + event EventHandler Disposed; IUnitOfWorkOptions Options { get; } @@ -36,5 +36,9 @@ namespace Volo.Abp.Uow void Complete(); Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken)); + + void Rollback(); + + Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs index b481bd5da4..aac08f3fa2 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -20,9 +19,9 @@ namespace Volo.Abp.Uow public string ReservationName { get; set; } - public event EventHandler Completed; + public event EventHandler Completed; public event EventHandler Failed; - public event EventHandler Disposed; + public event EventHandler Disposed; public IServiceProvider ServiceProvider { get; } @@ -33,6 +32,7 @@ namespace Volo.Abp.Uow private Exception _exception; private bool _isCompleted; private bool _isDisposed; + private bool _isRolledback; public UnitOfWork(IServiceProvider serviceProvider, IOptions options) { @@ -90,7 +90,13 @@ namespace Volo.Abp.Uow public void Complete() { + if (_isRolledback) + { + return; + } + PreventMultipleComplete(); + try { SaveChanges(); @@ -106,6 +112,11 @@ namespace Volo.Abp.Uow public async Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken)) { + if (_isRolledback) + { + return; + } + PreventMultipleComplete(); try @@ -121,6 +132,52 @@ namespace Volo.Abp.Uow } } + public void Rollback() + { + if (_isRolledback) + { + return; + } + + _isRolledback = true; + + foreach (var databaseApi in _databaseApis.Values) + { + (databaseApi as ISupportsRollback)?.Rollback(); + } + + foreach (var transactionApi in _transactionApis.Values) + { + (transactionApi as ISupportsRollback)?.Rollback(); + } + } + + public async Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + if (_isRolledback) + { + return; + } + + _isRolledback = true; + + foreach (var databaseApi in _databaseApis.Values) + { + if (databaseApi is ISupportsRollback) + { + await (databaseApi as ISupportsRollback).RollbackAsync(cancellationToken); + } + } + + foreach (var transactionApi in _transactionApis.Values) + { + if (transactionApi is ISupportsRollback) + { + await (transactionApi as ISupportsRollback).RollbackAsync(cancellationToken); + } + } + } + public IDatabaseApi FindDatabaseApi(string key) { return _databaseApis.GetOrDefault(key); @@ -177,17 +234,17 @@ namespace Volo.Abp.Uow protected virtual void OnCompleted() { - Completed.InvokeSafely(this); + Completed.InvokeSafely(this, new UnitOfWorkEventArgs(this)); } - protected virtual void OnFailed(Exception exception) + protected virtual void OnFailed() { - Failed.InvokeSafely(this, new UnitOfWorkFailedEventArgs(exception)); + Failed.InvokeSafely(this, new UnitOfWorkFailedEventArgs(this, _exception, _isRolledback)); } protected virtual void OnDisposed() { - Disposed.InvokeSafely(this); + Disposed.InvokeSafely(this, new UnitOfWorkEventArgs(this)); } public void Dispose() @@ -201,7 +258,7 @@ namespace Volo.Abp.Uow if (!_isCompleted || _exception != null) { - OnFailed(_exception); + OnFailed(); } OnDisposed(); diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkEventArgs.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkEventArgs.cs new file mode 100644 index 0000000000..9ab7c953f9 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkEventArgs.cs @@ -0,0 +1,20 @@ +using System; +using JetBrains.Annotations; + +namespace Volo.Abp.Uow +{ + public class UnitOfWorkEventArgs : EventArgs + { + /// + /// Reference to the unit of work related to this event. + /// + public IUnitOfWork UnitOfWork { get; } + + public UnitOfWorkEventArgs([NotNull] IUnitOfWork unitOfWork) + { + Check.NotNull(unitOfWork, nameof(unitOfWork)); + + UnitOfWork = unitOfWork; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs index e17dfa4bc0..4218a9693f 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs @@ -6,7 +6,7 @@ namespace Volo.Abp.Uow /// /// Used as event arguments on event. /// - public class UnitOfWorkFailedEventArgs : EventArgs + public class UnitOfWorkFailedEventArgs : UnitOfWorkEventArgs { /// /// Exception that caused failure. This is set only if an error occured during . @@ -16,13 +16,19 @@ namespace Volo.Abp.Uow [CanBeNull] public Exception Exception { get; } + /// + /// True, if the unit of work is manually rolled back. + /// + public bool IsRolledback { get; } + /// /// Creates a new object. /// - /// Exception that caused failure - public UnitOfWorkFailedEventArgs([CanBeNull] Exception exception) + public UnitOfWorkFailedEventArgs([NotNull] IUnitOfWork unitOfWork, [CanBeNull] Exception exception, bool isRolledback) + : base(unitOfWork) { Exception = exception; + IsRolledback = isRolledback; } } } diff --git a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs index 5c18c9a7ab..d13a964fc5 100644 --- a/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs +++ b/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Transaction_Tests.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Domain.Repositories; using Volo.Abp.TestApp.Domain; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.EntityFrameworkCore @@ -11,10 +12,12 @@ namespace Volo.Abp.EntityFrameworkCore public class Transaction_Tests : EntityFrameworkCoreTestBase { private readonly IRepository _personRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; public Transaction_Tests() { _personRepository = ServiceProvider.GetRequiredService>(); + _unitOfWorkManager = ServiceProvider.GetRequiredService(); } [Fact] @@ -31,7 +34,7 @@ namespace Volo.Abp.EntityFrameworkCore throw new Exception(exceptionMessage); }); } - catch (Exception e) when(e.Message == exceptionMessage) + catch (Exception e) when (e.Message == exceptionMessage) { } @@ -39,5 +42,21 @@ namespace Volo.Abp.EntityFrameworkCore var person = await _personRepository.FindAsync(personId); person.ShouldBeNull(); } + + [Fact] + public async Task Should_Rollback_Transaction_Manually() + { + var personId = Guid.NewGuid(); + + await WithUnitOfWorkAsync(async () => + { + _unitOfWorkManager.Current.ShouldNotBeNull(); + await _personRepository.InsertAsync(new Person(personId, "Adam", 42)); + await _unitOfWorkManager.Current.RollbackAsync(); + }); + + var person = await _personRepository.FindAsync(personId); + person.ShouldBeNull(); + } } } diff --git a/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs b/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs index 04c061d284..a9b18b9978 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs +++ b/test/Volo.Abp.Tests/Volo/Abp/Uow/UnitOfWork_Events_Tests.cs @@ -108,5 +108,36 @@ namespace Volo.Abp.Uow failed.ShouldBeTrue(); disposed.ShouldBeTrue(); } + + [InlineData(true)] + [InlineData(false)] + [Theory] + public void Should_Trigger_Failed_If_Rolled_Back(bool callComplete) + { + var completed = false; + var failed = false; + var disposed = false; + + Assert.Throws(() => + { + using (var uow = _unitOfWorkManager.Begin()) + { + uow.Completed += (sender, args) => completed = true; + uow.Failed += (sender, args) => { failed = true; args.IsRolledback.ShouldBeTrue(); }; + uow.Disposed += (sender, args) => disposed = true; + + uow.Rollback(); + + if (callComplete) + { + uow.Complete(); + } + } + }).Message.ShouldBe("test exception"); + + completed.ShouldBeFalse(); + failed.ShouldBeTrue(); + disposed.ShouldBeTrue(); + } } }