Browse Source

Improved unit of work.

pull/113/head
Halil İbrahim Kalkan 9 years ago
parent
commit
6a3f864b73
  1. 2
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  2. 14
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUnitOfWorkMiddleware.cs
  3. 36
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
  4. 20
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs
  5. 29
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkAttribute.cs
  6. 9
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs
  7. 30
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/UnitOfWorkTestController.cs
  8. 3
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController.cs
  9. 42
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/TestUnitOfWork.cs
  10. 11
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/TestUnitOfWorkConfig.cs
  11. 27
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkMiddleware_Exception_Rollback_Tests.cs
  12. 59
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestController.cs
  13. 3
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs
  14. 3
      test/Volo.Abp.MemoryDb.Tests/Volo/Abp/TestApp/MemoryDb/TestAppMemoryDbContext.cs

2
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

@ -62,7 +62,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
context.Exception = null; //Handled!
}
private int GetStatusCode(ExceptionContext context)
private static int GetStatusCode(ExceptionContext context)
{
if (context.Exception is AbpAuthorizationException)
{

14
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUnitOfWorkMiddleware.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Volo.Abp.Uow;
@ -18,7 +19,16 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
using (var uow = unitOfWorkManager.Reserve(AbpUowActionFilter.UnitOfWorkReservationName))
{
await _next(httpContext);
await uow.CompleteAsync(httpContext.RequestAborted);
try
{
await uow.CompleteAsync(httpContext.RequestAborted);
}
catch (Exception e)
{
throw;
}
}
}
}

36
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs

@ -42,31 +42,53 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
if (_unitOfWorkManager.TryBeginReserved(UnitOfWorkReservationName, options))
{
await next();
var result = await next();
if (!Succeed(result))
{
await RollbackAsync(context);
}
return;
}
using (var uow = _unitOfWorkManager.Begin(options))
{
var result = await next();
if (result.Exception == null || result.ExceptionHandled)
if (Succeed(result))
{
await uow.CompleteAsync(context.HttpContext.RequestAborted);
}
}
}
private UnitOfWorkOptions CreateOptions(ActionExecutingContext context, UnitOfWorkAttribute unitOfWorkAttr)
private UnitOfWorkOptions CreateOptions(ActionExecutingContext context, UnitOfWorkAttribute unitOfWorkAttribute)
{
var options = new UnitOfWorkOptions();
unitOfWorkAttr?.SetOptions(options);
unitOfWorkAttribute?.SetOptions(options);
options.IsTransactional = _defaultOptions.CalculateIsTransactional(
autoValue: !string.Equals(context.HttpContext.Request.Method, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase)
);
if (unitOfWorkAttribute?.IsTransactional == null)
{
options.IsTransactional = _defaultOptions.CalculateIsTransactional(
autoValue: !string.Equals(context.HttpContext.Request.Method, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase)
);
}
return options;
}
private async Task RollbackAsync(ActionExecutingContext context)
{
var currentUow = _unitOfWorkManager.Current;
if (currentUow != null)
{
await currentUow.RollbackAsync(context.HttpContext.RequestAborted);
}
}
private static bool Succeed(ActionExecutedContext result)
{
return result.Exception == null || result.ExceptionHandled;
}
}
}

20
src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs

@ -43,7 +43,7 @@ namespace Volo.Abp.Uow
_transactionApis = new Dictionary<string, ITransactionApi>();
}
public void Initialize(UnitOfWorkOptions options)
public virtual void Initialize(UnitOfWorkOptions options)
{
Check.NotNull(options, nameof(options));
@ -56,7 +56,7 @@ namespace Volo.Abp.Uow
IsReserved = false;
}
public void Reserve(string reservationName)
public virtual void Reserve(string reservationName)
{
Check.NotNull(reservationName, nameof(reservationName));
@ -64,12 +64,12 @@ namespace Volo.Abp.Uow
IsReserved = true;
}
public void SetOuter(IUnitOfWork outer)
public virtual void SetOuter(IUnitOfWork outer)
{
Outer = outer;
}
public void SaveChanges()
public virtual void SaveChanges()
{
foreach (var databaseApi in _databaseApis.Values)
{
@ -77,7 +77,7 @@ namespace Volo.Abp.Uow
}
}
public async Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
public virtual async Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
foreach (var databaseApi in _databaseApis.Values)
{
@ -88,7 +88,7 @@ namespace Volo.Abp.Uow
}
}
public void Complete()
public virtual void Complete()
{
if (_isRolledback)
{
@ -110,7 +110,7 @@ namespace Volo.Abp.Uow
}
}
public async Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken))
public virtual async Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (_isRolledback)
{
@ -132,7 +132,7 @@ namespace Volo.Abp.Uow
}
}
public void Rollback()
public virtual void Rollback()
{
if (_isRolledback)
{
@ -152,7 +152,7 @@ namespace Volo.Abp.Uow
}
}
public async Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken))
public virtual async Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (_isRolledback)
{
@ -247,7 +247,7 @@ namespace Volo.Abp.Uow
Disposed.InvokeSafely(this, new UnitOfWorkEventArgs(this));
}
public void Dispose()
public virtual void Dispose()
{
if (_isDisposed)
{

29
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkAttribute.cs

@ -1,5 +1,7 @@
using System;
using System.Data;
using System.Threading;
using JetBrains.Annotations;
namespace Volo.Abp.Uow
{
@ -37,7 +39,32 @@ namespace Volo.Abp.Uow
/// </summary>
public bool IsDisabled { get; set; }
public virtual void SetOptions(UnitOfWorkOptions options)
public UnitOfWorkAttribute()
{
}
public UnitOfWorkAttribute(bool isTransactional)
{
IsTransactional = isTransactional;
}
public UnitOfWorkAttribute(bool isTransactional, IsolationLevel isolationLevel)
{
IsTransactional = isTransactional;
IsolationLevel = isolationLevel;
}
public UnitOfWorkAttribute(bool isTransactional, IsolationLevel isolationLevel, TimeSpan timeout)
{
IsTransactional = isTransactional;
IsolationLevel = isolationLevel;
Timeout = timeout;
}
//TODO: More constructors!
internal virtual void SetOptions(UnitOfWorkOptions options)
{
if (IsTransactional.HasValue)
{

9
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs

@ -55,9 +55,12 @@ namespace Volo.Abp.Uow
unitOfWorkAttribute?.SetOptions(options);
options.IsTransactional = _defaultOptions.CalculateIsTransactional(
autoValue: !invocation.Method.Name.StartsWith("Get", StringComparison.InvariantCultureIgnoreCase)
);
if (unitOfWorkAttribute?.IsTransactional == null)
{
options.IsTransactional = _defaultOptions.CalculateIsTransactional(
autoValue: !invocation.Method.Name.StartsWith("Get", StringComparison.InvariantCultureIgnoreCase)
);
}
return options;
}

30
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/UnitOfWorkTestController.cs

@ -1,30 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Shouldly;
using Volo.Abp.AspNetCore.Mvc;
namespace Volo.Abp.AspNetCore.App
{
[Route("api/unitofwork-test")]
public class UnitOfWorkTestController : AbpController
{
[HttpGet]
[Route("ActionRequiresUow")]
public ActionResult ActionRequiresUow()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse();
return Content("OK");
}
[HttpPost]
[Route("ActionRequiresUowPost")]
public ActionResult ActionRequiresUowPost()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue();
return Content("OK");
}
}
}

3
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/ExceptionTestController.cs → test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController.cs

@ -1,8 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Ui;
namespace Volo.Abp.AspNetCore.App
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
[Route("api/exception-test")]
public class ExceptionTestController : AbpController

42
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/TestUnitOfWork.cs

@ -0,0 +1,42 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Ui;
using Volo.Abp.Uow;
namespace Volo.Abp.AspNetCore.Mvc.Uow
{
[Dependency(ReplaceServices = true)]
public class TestUnitOfWork : UnitOfWork
{
private readonly TestUnitOfWorkConfig _config;
public TestUnitOfWork(IServiceProvider serviceProvider, IOptions<UnitOfWorkDefaultOptions> options, TestUnitOfWorkConfig config)
: base(serviceProvider, options)
{
_config = config;
}
public override void Complete()
{
ThrowExceptionIfRequested();
base.Complete();
}
public override Task CompleteAsync(CancellationToken cancellationToken = default(CancellationToken))
{
ThrowExceptionIfRequested();
return base.CompleteAsync(cancellationToken);
}
private void ThrowExceptionIfRequested()
{
if (_config.ThrowExceptionOnComplete)
{
throw new UserFriendlyException(TestUnitOfWorkConfig.ExceptionOnCompleteMessage);
}
}
}
}

11
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/TestUnitOfWorkConfig.cs

@ -0,0 +1,11 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.Uow
{
public class TestUnitOfWorkConfig : ISingletonDependency
{
public const string ExceptionOnCompleteMessage = "TestUnitOfWork configured for exception";
public bool ThrowExceptionOnComplete { get; set; }
}
}

27
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkMiddleware_Exception_Rollback_Tests.cs

@ -0,0 +1,27 @@
using System.Net;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Http;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.Uow
{
public class UnitOfWorkMiddleware_Exception_Rollback_Tests : AspNetCoreMvcTestBase
{
[Fact]
public async Task Should_Rollback_Transaction_For_Handled_Exceptions()
{
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/unitofwork-test/HandledException", HttpStatusCode.InternalServerError);
result.Error.ShouldNotBeNull();
result.Error.Message.ShouldBe("This is a sample exception!");
}
[Fact]
public async Task Should_Gracefully_Handle_Exceptions_On_Complete()
{
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/api/unitofwork-test/ExceptionOnComplete", HttpStatusCode.InternalServerError);
result.Error.ShouldNotBeNull();
result.Error.Message.ShouldBe(TestUnitOfWorkConfig.ExceptionOnCompleteMessage);
}
}
}

59
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestController.cs

@ -0,0 +1,59 @@
using Microsoft.AspNetCore.Mvc;
using Shouldly;
using Volo.Abp.Ui;
using Volo.Abp.Uow;
namespace Volo.Abp.AspNetCore.Mvc.Uow
{
[Route("api/unitofwork-test")]
public class UnitOfWorkTestController : AbpController
{
private readonly TestUnitOfWorkConfig _testUnitOfWorkConfig;
public UnitOfWorkTestController(TestUnitOfWorkConfig testUnitOfWorkConfig)
{
_testUnitOfWorkConfig = testUnitOfWorkConfig;
}
[HttpGet]
[Route("ActionRequiresUow")]
public ActionResult ActionRequiresUow()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse();
return Content("OK");
}
[HttpPost]
[Route("ActionRequiresUowPost")]
public ActionResult ActionRequiresUowPost()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue();
return Content("OK");
}
[HttpGet]
[Route("HandledException")]
[UnitOfWork(isTransactional: true)]
public void HandledException()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue();
throw new UserFriendlyException("This is a sample exception!");
}
[HttpGet]
[Route("ExceptionOnComplete")]
public void ExceptionOnComplete()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse();
_testUnitOfWorkConfig.ThrowExceptionOnComplete = true;
}
}
}

3
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/ValidationTestController.cs → test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs

@ -2,9 +2,8 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Shouldly;
using Volo.Abp.AspNetCore.Mvc;
namespace Volo.Abp.AspNetCore.App
namespace Volo.Abp.AspNetCore.Mvc.Validation
{
[Route("api/validation-test")]
public class ValidationTestController : AbpController

3
test/Volo.Abp.MemoryDb.Tests/Volo/Abp/TestApp/MemoryDb/TestAppMemoryDbContext.cs

@ -7,8 +7,7 @@ namespace Volo.Abp.TestApp.MemoryDb
{
public class TestAppMemoryDbContext : MemoryDbContext
{
private static readonly Type[] EntityTypeList = new Type[]
{
private static readonly Type[] EntityTypeList = {
typeof(Person)
};

Loading…
Cancel
Save