Added events to UOW.

pull/81/head
Halil İbrahim Kalkan 9 years ago
parent 684453cab1
commit 8a68714e03

@ -2,11 +2,16 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.ExtensionMethods;
namespace Volo.Abp.Uow
{
internal class ChildUnitOfWork : IUnitOfWork
{
public event EventHandler Completed;
public event EventHandler<UnitOfWorkFailedEventArgs> Failed;
public event EventHandler Disposed;
public IServiceProvider ServiceProvider => _parent.ServiceProvider;
private readonly IUnitOfWork _parent;
@ -16,6 +21,10 @@ namespace Volo.Abp.Uow
Check.NotNull(parent, nameof(parent));
_parent = parent;
_parent.Completed += (sender, args) => { Completed.InvokeSafely(sender, args); };
_parent.Failed += (sender, args) => { Failed.InvokeSafely(sender, args); };
_parent.Disposed += (sender, args) => { Disposed.InvokeSafely(sender, args); };
}
public void SaveChanges()
@ -30,7 +39,7 @@ namespace Volo.Abp.Uow
public void Complete()
{
_parent.Complete();
}
public Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken))

@ -1,9 +1,6 @@
using JetBrains.Annotations;
namespace Volo.Abp.Uow
namespace Volo.Abp.Uow
{
public interface IAmbientUnitOfWork : IUnitOfWorkAccessor
{
void SetUnitOfWork([CanBeNull] IUnitOfWork unitOfWork);
}
}

@ -7,6 +7,12 @@ namespace Volo.Abp.Uow
//Find a better naming :(
public interface IBasicUnitOfWork : IDisposable
{
event EventHandler Completed;
event EventHandler<UnitOfWorkFailedEventArgs> Failed;
event EventHandler Disposed;
void SaveChanges();
Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));

@ -2,5 +2,6 @@
{
public interface IUnitOfWork : IBasicUnitOfWork, IDatabaseApiContainer
{
}
}

@ -6,5 +6,7 @@ namespace Volo.Abp.Uow
{
[CanBeNull]
IUnitOfWork UnitOfWork { get; }
void SetUnitOfWork([CanBeNull] IUnitOfWork unitOfWork);
}
}

@ -3,22 +3,27 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.DependencyInjection;
using Volo.ExtensionMethods;
using Volo.ExtensionMethods.Collections.Generic;
namespace Volo.Abp.Uow
{
public class UnitOfWork : IUnitOfWork, ITransientDependency
{
public event EventHandler Completed;
public event EventHandler<UnitOfWorkFailedEventArgs> Failed;
public event EventHandler Disposed;
public IServiceProvider ServiceProvider { get; }
private readonly Dictionary<string, IDatabaseApi> _databaseApis;
private readonly IAmbientUnitOfWork _ambientUnitOfWork;
private Exception _exception;
private bool _isCompleted;
private bool _isDisposed;
public UnitOfWork(IServiceProvider serviceProvider, IAmbientUnitOfWork ambientUnitOfWork)
public UnitOfWork(IServiceProvider serviceProvider)
{
_ambientUnitOfWork = ambientUnitOfWork;
ServiceProvider = serviceProvider;
_databaseApis = new Dictionary<string, IDatabaseApi>();
@ -32,7 +37,13 @@ namespace Volo.Abp.Uow
}
_isDisposed = true;
_ambientUnitOfWork.SetUnitOfWork(null);
if (!_isCompleted || _exception != null)
{
OnFailed(_exception);
}
OnDisposed();
}
public void SaveChanges()
@ -56,12 +67,33 @@ namespace Volo.Abp.Uow
public void Complete()
{
SaveChanges();
PreventMultipleComplete();
try
{
SaveChanges();
OnCompleted();
}
catch (Exception ex)
{
_exception = ex;
throw;
}
}
public async Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken))
{
await SaveChangesAsync(cancellationToken);
PreventMultipleComplete();
try
{
await SaveChangesAsync(cancellationToken);
OnCompleted();
}
catch (Exception ex)
{
_exception = ex;
throw;
}
}
public IDatabaseApi FindDatabaseApi(string id)
@ -76,5 +108,30 @@ namespace Volo.Abp.Uow
return _databaseApis.GetOrAdd(id, factory);
}
protected virtual void OnCompleted()
{
Completed.InvokeSafely(this);
}
protected virtual void OnFailed(Exception exception)
{
Failed.InvokeSafely(this, new UnitOfWorkFailedEventArgs(exception));
}
protected virtual void OnDisposed()
{
Disposed.InvokeSafely(this);
}
private void PreventMultipleComplete()
{
if (_isCompleted)
{
throw new AbpException("Complete is called before!");
}
_isCompleted = true;
}
}
}

@ -0,0 +1,28 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.Uow
{
/// <summary>
/// Used as event arguments on <see cref="IUnitOfWork.Failed"/> event.
/// </summary>
public class UnitOfWorkFailedEventArgs : EventArgs
{
/// <summary>
/// Exception that caused failure. This is set only if an error occured during <see cref="IBasicUnitOfWork.Complete"/>.
/// Can be null if there is no exception, but <see cref="IBasicUnitOfWork.Complete"/> is not called.
/// Can be null if another exception occurred during the UOW.
/// </summary>
[CanBeNull]
public Exception Exception { get; private set; }
/// <summary>
/// Creates a new <see cref="UnitOfWorkFailedEventArgs"/> object.
/// </summary>
/// <param name="exception">Exception that caused failure</param>
public UnitOfWorkFailedEventArgs([CanBeNull] Exception exception)
{
Exception = exception;
}
}
}

@ -25,10 +25,13 @@ namespace Volo.Abp.Uow
return new ChildUnitOfWork(_ambientUnitOfWork.UnitOfWork);
}
var parentUow = _ambientUnitOfWork.UnitOfWork;
var scope = _serviceProvider.CreateScope();
IUnitOfWork unitOfWork;
try
{
_ambientUnitOfWork.SetUnitOfWork(scope.ServiceProvider.GetRequiredService<IUnitOfWork>());
unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>();
}
catch
{
@ -36,7 +39,28 @@ namespace Volo.Abp.Uow
throw;
}
Debug.Assert(_ambientUnitOfWork.UnitOfWork != null, "_ambientUnitOfWork.UnitOfWork can not be null since it's set by _ambientUnitOfWork.SetUnitOfWork method!");
_ambientUnitOfWork.SetUnitOfWork(unitOfWork);
Debug.Assert(
_ambientUnitOfWork.UnitOfWork != null,
"_ambientUnitOfWork.UnitOfWork can not be null since it's set by _ambientUnitOfWork.SetUnitOfWork method!"
);
unitOfWork.Completed += (sender, args) =>
{
_ambientUnitOfWork.SetUnitOfWork(parentUow);
};
unitOfWork.Failed += (sender, args) =>
{
_ambientUnitOfWork.SetUnitOfWork(parentUow);
};
unitOfWork.Disposed += (sender, args) =>
{
scope.Dispose();
};
return _ambientUnitOfWork.UnitOfWork;
}
}

@ -0,0 +1,113 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.TestBase;
using Xunit;
namespace Volo.Abp.Uow
{
public class UnitOfWork_Events_Tests : AbpIntegratedTest<IndependentEmptyModule>
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
public UnitOfWork_Events_Tests()
{
_unitOfWorkManager = ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
}
[Fact]
public void Should_Trigger_Complete_On_Success()
{
var completed = false;
var disposed = false;
using (var uow = _unitOfWorkManager.Begin())
{
uow.Completed += (sender, args) => completed = true;
uow.Disposed += (sender, args) => disposed = true;
uow.Complete();
completed.ShouldBeTrue();
}
disposed.ShouldBeTrue();
}
[Fact]
public void Should_Trigger_Complete_On_Success_In_Child_Uow()
{
var completed = false;
var disposed = false;
using (var uow = _unitOfWorkManager.Begin())
{
using (var childUow = _unitOfWorkManager.Begin())
{
childUow.Completed += (sender, args) => completed = true;
uow.Disposed += (sender, args) => disposed = true;
childUow.Complete();
completed.ShouldBeFalse(); //Parent has not been completed yet!
disposed.ShouldBeFalse();
}
completed.ShouldBeFalse(); //Parent has not been completed yet!
disposed.ShouldBeFalse();
uow.Complete();
completed.ShouldBeTrue(); //It's completed now!
disposed.ShouldBeFalse(); //But not disposed yet!
}
disposed.ShouldBeTrue();
}
[Fact]
public void Should_Not_Trigger_Complete_If_Uow_Is_Not_Completed()
{
var completed = false;
var failed = false;
var disposed = false;
using (var uow = _unitOfWorkManager.Begin())
{
uow.Completed += (sender, args) => completed = true;
uow.Failed += (sender, args) => failed = true;
uow.Disposed += (sender, args) => disposed = true;
}
completed.ShouldBeFalse();
failed.ShouldBeTrue();
disposed.ShouldBeTrue();
}
[Fact]
public void Should_Trigger_Failed_If_Uow_Throws_Exception()
{
var completed = false;
var failed = false;
var disposed = false;
Assert.Throws<Exception>(new Action(() =>
{
using (var uow = _unitOfWorkManager.Begin())
{
uow.Completed += (sender, args) => completed = true;
uow.Failed += (sender, args) => failed = true;
uow.Disposed += (sender, args) => disposed = true;
throw new Exception("test exception");
}
})).Message.ShouldBe("test exception");
completed.ShouldBeFalse();
failed.ShouldBeTrue();
disposed.ShouldBeTrue();
}
}
}
Loading…
Cancel
Save