diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs index 5b005facee..7a98a7729e 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs @@ -1,5 +1,8 @@ -using System.Threading.Tasks; +using System; +using System.Net.Http; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.Uow; @@ -10,10 +13,12 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow public const string UnitOfWorkReservationName = "_AbpActionUnitOfWork"; private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly UnitOfWorkDefaultOptions _defaultOptions; - public AbpUowActionFilter(IUnitOfWorkManager unitOfWorkManager) + public AbpUowActionFilter(IUnitOfWorkManager unitOfWorkManager, IOptions options) { _unitOfWorkManager = unitOfWorkManager; + _defaultOptions = options.Value; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) @@ -24,7 +29,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow return; } - var unitOfWorkAttr = UnitOfWorkHelper.GetUnitOfWorkAttributeOrNull(context.ActionDescriptor.GetMethodInfo()); + var methodInfo = context.ActionDescriptor.GetMethodInfo(); + var unitOfWorkAttr = UnitOfWorkHelper.GetUnitOfWorkAttributeOrNull(methodInfo); if (unitOfWorkAttr?.IsDisabled == true) { @@ -32,9 +38,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow return; } - var options = new UnitOfWorkOptions(); - - unitOfWorkAttr?.SetOptions(options); + var options = CreateOptions(context, unitOfWorkAttr); if (_unitOfWorkManager.TryBeginReserved(UnitOfWorkReservationName, options)) { @@ -51,5 +55,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow } } } + + private UnitOfWorkOptions CreateOptions(ActionExecutingContext context, UnitOfWorkAttribute unitOfWorkAttr) + { + var options = new UnitOfWorkOptions(); + + unitOfWorkAttr?.SetOptions(options); + + options.IsTransactional = _defaultOptions.CalculateIsTransactional( + autoValue: !string.Equals(context.HttpContext.Request.Method, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) + ); + + return options; + } } } diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkDefaultOptions.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkDefaultOptions.cs index 7296eba767..cb7288549d 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkDefaultOptions.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkDefaultOptions.cs @@ -1,5 +1,6 @@ using System; using System.Data; +using System.Reflection; namespace Volo.Abp.Uow { @@ -30,5 +31,20 @@ namespace Volo.Abp.Uow return options; } + + internal bool CalculateIsTransactional(bool autoValue) + { + switch (TransactionBehavior) + { + case UnitOfWorkTransactionBehavior.Enabled: + return true; + case UnitOfWorkTransactionBehavior.Disabled: + return false; + case UnitOfWorkTransactionBehavior.Auto: + return autoValue; + default: + throw new AbpException("Not implemented TransactionBehavior value: " + TransactionBehavior); + } + } } } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkHelper.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkHelper.cs index 4f1a50f429..47d42aa322 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkHelper.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkHelper.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Reflection; +using JetBrains.Annotations; using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; @@ -25,13 +26,16 @@ namespace Volo.Abp.Uow return false; } - public static bool IsUnitOfWorkMethod(MethodInfo methodInfo) + public static bool IsUnitOfWorkMethod([NotNull] MethodInfo methodInfo, [CanBeNull] out UnitOfWorkAttribute unitOfWorkAttribute) { + Check.NotNull(methodInfo, nameof(methodInfo)); + //Method declaration var attrs = methodInfo.GetCustomAttributes(true).OfType().ToArray(); if (attrs.Any()) { - return true; + unitOfWorkAttribute = attrs.First(); + return !unitOfWorkAttribute.IsDisabled; } if (methodInfo.DeclaringType != null) @@ -40,17 +44,20 @@ namespace Volo.Abp.Uow attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType().ToArray(); if (attrs.Any()) { - return true; + unitOfWorkAttribute = attrs.First(); + return !unitOfWorkAttribute.IsDisabled; } - //Conventional classes + //Conventional classes //TODO: Make this extendible to add new conventions! if (typeof(IApplicationService).GetTypeInfo().IsAssignableFrom(methodInfo.DeclaringType) || typeof(IRepository).GetTypeInfo().IsAssignableFrom(methodInfo.DeclaringType)) { + unitOfWorkAttribute = null; return true; } } + unitOfWorkAttribute = null; return false; } diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs index 1bf813905a..b95173edde 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInterceptor.cs @@ -1,4 +1,8 @@ -using System.Threading.Tasks; +using System; +using System.Net.Http; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; @@ -7,40 +11,55 @@ namespace Volo.Abp.Uow public class UnitOfWorkInterceptor : AbpInterceptor, ITransientDependency { private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly UnitOfWorkDefaultOptions _defaultOptions; - public UnitOfWorkInterceptor(IUnitOfWorkManager unitOfWorkManager) + public UnitOfWorkInterceptor(IUnitOfWorkManager unitOfWorkManager, IOptions options) { _unitOfWorkManager = unitOfWorkManager; + _defaultOptions = options.Value; } public override void Intercept(IAbpMethodInvocation invocation) { - if (!UnitOfWorkHelper.IsUnitOfWorkMethod(invocation.Method)) + if (!UnitOfWorkHelper.IsUnitOfWorkMethod(invocation.Method, out var unitOfWorkAttribute)) { invocation.Proceed(); return; } - using (var uow = _unitOfWorkManager.Begin()) + using (var uow = _unitOfWorkManager.Begin(CreateOptions(invocation, unitOfWorkAttribute))) { invocation.Proceed(); uow.Complete(); } } - public override async Task InterceptAsync(IAbpMethodInvocation invocation) + public override async Task InterceptAsync(IAbpMethodInvocation invocation) { - if (!UnitOfWorkHelper.IsUnitOfWorkMethod(invocation.Method)) + if (!UnitOfWorkHelper.IsUnitOfWorkMethod(invocation.Method, out var unitOfWorkAttribute)) { await invocation.ProceedAsync(); return; } - using (var uow = _unitOfWorkManager.Begin()) + using (var uow = _unitOfWorkManager.Begin(CreateOptions(invocation, unitOfWorkAttribute))) { await invocation.ProceedAsync(); await uow.CompleteAsync(); } } + + private UnitOfWorkOptions CreateOptions(IAbpMethodInvocation invocation, [CanBeNull] UnitOfWorkAttribute unitOfWorkAttribute) + { + var options = new UnitOfWorkOptions(); + + unitOfWorkAttribute?.SetOptions(options); + + options.IsTransactional = _defaultOptions.CalculateIsTransactional( + autoValue: !invocation.Method.Name.StartsWith("Get", StringComparison.InvariantCultureIgnoreCase) + ); + + return options; + } } } diff --git a/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/AbpIdentityApplicationTestModule.cs b/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/AbpIdentityApplicationTestModule.cs index 6c3e5cd2e8..d560cd69b4 100644 --- a/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/AbpIdentityApplicationTestModule.cs +++ b/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/AbpIdentityApplicationTestModule.cs @@ -5,6 +5,7 @@ using Volo.Abp.Autofac; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Modularity; +using Volo.Abp.Uow; namespace Volo.Abp.Identity { @@ -26,6 +27,11 @@ namespace Volo.Abp.Identity context.DbContextOptions.UseInMemoryDatabase(databaseName); }); }); + + services.Configure(options => + { + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context)