diff --git a/src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs b/src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs index 3205d629a4..c68ddcc8a8 100644 --- a/src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs +++ b/src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs @@ -3,8 +3,8 @@ using System.Threading.Tasks; using AbpDesk.Tickets.Dtos; using Volo.Abp.Application.Services.Dtos; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Linq; using Volo.Abp.Linq.Extensions; -using Volo.Abp.Uow; using Volo.ExtensionMethods; namespace AbpDesk.Tickets @@ -12,33 +12,32 @@ namespace AbpDesk.Tickets public class TicketAppService : ITicketAppService { private readonly IQueryableRepository _ticketRepository; - private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IAsyncQueryableExecuter _asyncQueryableExecuter; public TicketAppService( IQueryableRepository ticketRepository, - IUnitOfWorkManager unitOfWorkManager + IAsyncQueryableExecuter asyncQueryableExecuter ) { _ticketRepository = ticketRepository; - _unitOfWorkManager = unitOfWorkManager; + _asyncQueryableExecuter = asyncQueryableExecuter; } public async Task> GetAll(GetAllTicketsInput input) { - //Use conventional unit of work for application services when it's available! - using (var unitOfWork = _unitOfWorkManager.Begin()) - { - var tickets = _ticketRepository - .WhereIf( - !input.Filter.IsNullOrWhiteSpace(), - t => t.Title.Contains(input.Filter) || t.Body.Contains(input.Filter) - ).Select(t => new TicketDto { Id = t.Id, Title = t.Title, Body = t.Body }) - .ToList(); + var tickets = await _asyncQueryableExecuter.ToListAsync(_ticketRepository + .WhereIf( + !input.Filter.IsNullOrWhiteSpace(), + t => t.Title.Contains(input.Filter) || t.Body.Contains(input.Filter) + ) + .Select(t => new TicketDto + { + Id = t.Id, + Title = t.Title, + Body = t.Body + })); - await unitOfWork.CompleteAsync(); - - return new ListResultDto(tickets); - } + return new ListResultDto(tickets); } } } diff --git a/src/Volo.Abp/Volo/Abp/Threading/AsyncHelper.cs b/src/Volo.Abp/Volo/Abp/Threading/AsyncHelper.cs index a63d7704c2..bf6b5e060d 100644 --- a/src/Volo.Abp/Volo/Abp/Threading/AsyncHelper.cs +++ b/src/Volo.Abp/Volo/Abp/Threading/AsyncHelper.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.Threading /// Checks if given method is an async method. /// /// A method to check - public static bool IsAsyncMethod([NotNull] this MethodInfo method) + public static bool IsAsync([NotNull] this MethodInfo method) { return method.ReturnType == typeof(Task) || (method.ReturnType.GetTypeInfo().IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)); diff --git a/src/Volo.Abp/Volo/Abp/Threading/InternalAsyncHelper.cs b/src/Volo.Abp/Volo/Abp/Threading/InternalAsyncHelper.cs new file mode 100644 index 0000000000..0dabef6e86 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Threading/InternalAsyncHelper.cs @@ -0,0 +1,181 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; + +namespace Volo.Abp.Threading +{ + internal static class InternalAsyncHelper + { + public static async Task AwaitTaskWithFinally(Task actualReturnValue, Action finalAction) + { + Exception exception = null; + + try + { + await actualReturnValue; + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + finalAction(exception); + } + } + + public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func postAction, Action finalAction) + { + Exception exception = null; + + try + { + await actualReturnValue; + await postAction(); + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + finalAction(exception); + } + } + + public static async Task AwaitTaskWithPreActionAndPostActionAndFinally(Func actualReturnValue, Func preAction = null, Func postAction = null, Action finalAction = null) + { + Exception exception = null; + + try + { + if (preAction != null) + { + await preAction(); + } + + await actualReturnValue(); + + if (postAction != null) + { + await postAction(); + } + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + if (finalAction != null) + { + finalAction(exception); + } + } + } + + public static async Task AwaitTaskWithFinallyAndGetResult(Task actualReturnValue, Action finalAction) + { + Exception exception = null; + + try + { + return await actualReturnValue; + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + finalAction(exception); + } + } + + public static object CallAwaitTaskWithFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Action finalAction) + { + return typeof(InternalAsyncHelper) + .GetTypeInfo() + .GetMethod("AwaitTaskWithFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static) + .MakeGenericMethod(taskReturnType) + .Invoke(null, new object[] { actualReturnValue, finalAction }); + } + + public static async Task AwaitTaskWithPostActionAndFinallyAndGetResult(Task actualReturnValue, Func postAction, Action finalAction) + { + Exception exception = null; + + try + { + var result = await actualReturnValue; + await postAction(); + return result; + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + finalAction(exception); + } + } + + public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Func action, Action finalAction) + { + return typeof (InternalAsyncHelper) + .GetTypeInfo() + .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static) + .MakeGenericMethod(taskReturnType) + .Invoke(null, new object[] { actualReturnValue, action, finalAction }); + } + + public static async Task AwaitTaskWithPreActionAndPostActionAndFinallyAndGetResult(Func> actualReturnValue, Func preAction = null, Func postAction = null, Action finalAction = null) + { + Exception exception = null; + + try + { + if (preAction != null) + { + await preAction(); + } + + var result = await actualReturnValue(); + + if (postAction != null) + { + await postAction(); + } + + return result; + } + catch (Exception ex) + { + exception = ex; + throw; + } + finally + { + if (finalAction != null) + { + finalAction(exception); + } + } + } + + public static object CallAwaitTaskWithPreActionAndPostActionAndFinallyAndGetResult(Type taskReturnType, Func actualReturnValue, Func preAction = null, Func postAction = null, Action finalAction = null) + { + return typeof(InternalAsyncHelper) + .GetTypeInfo() + .GetMethod("AwaitTaskWithPreActionAndPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static) + .MakeGenericMethod(taskReturnType) + .Invoke(null, new object[] { actualReturnValue, preAction, postAction, finalAction }); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/IBasicUnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/IBasicUnitOfWork.cs index ed75210680..60038cae2b 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/IBasicUnitOfWork.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/IBasicUnitOfWork.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; namespace Volo.Abp.Uow { - //Find a better naming :( + //TODO: Find a better naming :( public interface IBasicUnitOfWork : IDisposable { event EventHandler Completed; diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs index 3382047296..46875cc933 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs @@ -1,13 +1,74 @@ -using Volo.Abp.DynamicProxy; +using System.Threading.Tasks; +using Volo.Abp.DynamicProxy; +using Volo.Abp.Threading; using Volo.DependencyInjection; namespace Volo.Abp.Uow { public class UnitOfWorkInterceptor : IAbpInterceptor, ITransientDependency { + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public UnitOfWorkInterceptor(IUnitOfWorkManager unitOfWorkManager) + { + _unitOfWorkManager = unitOfWorkManager; + } + + public void Intercept(IAbpMethodInvocation invocation) { - invocation.Proceed(); + //TODO: Check UOW attribute and other conditions! + + if (invocation.Method.IsAsync()) + { + PerformAsyncUow(invocation); + } + else + { + PerformSyncUow(invocation); + } + } + + private void PerformSyncUow(IAbpMethodInvocation invocation) + { + using (var uow = _unitOfWorkManager.Begin()) + { + invocation.Proceed(); + uow.Complete(); + } + } + + private void PerformAsyncUow(IAbpMethodInvocation invocation) + { + var uow = _unitOfWorkManager.Begin(); + + try + { + invocation.Proceed(); + } + catch + { + uow.Dispose(); + throw; + } + + if (invocation.Method.ReturnType == typeof(Task)) + { + invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally( + (Task)invocation.ReturnValue, + async () => await uow.CompleteAsync(), + exception => uow.Dispose() + ); + } + else //Task + { + invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult( + invocation.Method.ReturnType.GenericTypeArguments[0], + invocation.ReturnValue, + async () => await uow.CompleteAsync(), + exception => uow.Dispose() + ); + } } } } diff --git a/test/Volo.Abp.Tests/Volo/Abp/Threading/AsyncHelper_Tests.cs b/test/Volo.Abp.Tests/Volo/Abp/Threading/AsyncHelper_Tests.cs index bc64357509..1b7b7eacd2 100644 --- a/test/Volo.Abp.Tests/Volo/Abp/Threading/AsyncHelper_Tests.cs +++ b/test/Volo.Abp.Tests/Volo/Abp/Threading/AsyncHelper_Tests.cs @@ -26,17 +26,17 @@ namespace Volo.Abp.Threading GetType().GetMethod( "MyTaskWithoutReturnValueAsync", BindingFlags.NonPublic | BindingFlags.Static - ).IsAsyncMethod().ShouldBe(true); + ).IsAsync().ShouldBe(true); GetType().GetMethod( "MyTaskWithReturnValueAsync", BindingFlags.NonPublic | BindingFlags.Static - ).IsAsyncMethod().ShouldBe(true); + ).IsAsync().ShouldBe(true); GetType().GetMethod( "MyTaskWithReturnValue2", BindingFlags.NonPublic | BindingFlags.Instance - ).IsAsyncMethod().ShouldBe(false); + ).IsAsync().ShouldBe(false); } private static async Task MyTaskWithoutReturnValueAsync()