Browse Source

Improvements on UOW.

pull/112/head
Halil İbrahim Kalkan 9 years ago
parent
commit
f12c0ccf6c
  1. 16
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
  2. 25
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs
  3. 27
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactoryContext.cs
  4. 22
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs
  5. 12
      src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs
  6. 4
      src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs
  7. 4
      src/Volo.Abp/Volo/Abp/Uow/IUnitOfWorkManager.cs
  8. 7
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs
  9. 43
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkAttribute.cs
  10. 18
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkHelper.cs
  11. 8
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs
  12. 16
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManagerExtensions.cs
  13. 50
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkStartOptions.cs

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

@ -24,15 +24,25 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
return; return;
} }
if (_unitOfWorkManager.TryBeginReserved(UnitOfWorkReservationName)) var unitOfWorkAttr = UnitOfWorkHelper.GetUnitOfWorkAttributeOrNull(context.ActionDescriptor.GetMethodInfo());
if (unitOfWorkAttr?.IsDisabled == true)
{ {
await next(); await next();
return; return;
} }
//TODO: Check if disabled. Get and apply attributes to control UOW. var options = new UnitOfWorkStartOptions();
unitOfWorkAttr?.SetOptions(options);
if (_unitOfWorkManager.TryBeginReserved(UnitOfWorkReservationName, options))
{
await next();
return;
}
using (var uow = _unitOfWorkManager.Begin()) using (var uow = _unitOfWorkManager.Begin(options))
{ {
var result = await next(); var result = await next();
if (result.Exception == null || result.ExceptionHandled) if (result.Exception == null || result.ExceptionHandled)

25
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs

@ -12,13 +12,13 @@ namespace Volo.Abp.EntityFrameworkCore.DependencyInjection
public static DbContextOptions<TDbContext> Create<TDbContext>(IServiceProvider serviceProvider) public static DbContextOptions<TDbContext> Create<TDbContext>(IServiceProvider serviceProvider)
where TDbContext : AbpDbContext<TDbContext> where TDbContext : AbpDbContext<TDbContext>
{ {
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
using (var scope = serviceProvider.CreateScope()) using (var scope = serviceProvider.CreateScope())
{ {
var creationContext = GetCreationContext<TDbContext>(scope.ServiceProvider);
var context = new AbpDbContextConfigurationContext<TDbContext>( var context = new AbpDbContextConfigurationContext<TDbContext>(
GetConnectionString(scope, connectionStringName), creationContext.ConnectionString,
connectionStringName, creationContext.ConnectionStringName,
scope.ServiceProvider scope.ServiceProvider
); );
@ -47,9 +47,22 @@ namespace Volo.Abp.EntityFrameworkCore.DependencyInjection
return scope.ServiceProvider.GetRequiredService<IOptions<AbpDbContextOptions>>().Value; return scope.ServiceProvider.GetRequiredService<IOptions<AbpDbContextOptions>>().Value;
} }
private static string GetConnectionString(IServiceScope scope, string connectionStringName) private static DbContextOptionsFactoryContext GetCreationContext<TDbContext>(IServiceProvider serviceProvider)
where TDbContext : AbpDbContext<TDbContext>
{ {
return scope.ServiceProvider.GetRequiredService<IConnectionStringResolver>().Resolve(connectionStringName); var context = DbContextOptionsFactoryContext.Current;
if (context != null)
{
return context;
}
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
var connectionString = serviceProvider.GetRequiredService<IConnectionStringResolver>().Resolve(connectionStringName);
return new DbContextOptionsFactoryContext(
connectionStringName,
connectionString
);
} }
} }
} }

27
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactoryContext.cs

@ -0,0 +1,27 @@
using System;
using System.Threading;
namespace Volo.Abp.EntityFrameworkCore.DependencyInjection
{
internal class DbContextOptionsFactoryContext
{
public static DbContextOptionsFactoryContext Current => _current.Value;
private static readonly AsyncLocal<DbContextOptionsFactoryContext> _current = new AsyncLocal<DbContextOptionsFactoryContext>();
public string ConnectionStringName { get; }
public string ConnectionString { get; }
public DbContextOptionsFactoryContext(string connectionStringName, string connectionString)
{
ConnectionStringName = connectionStringName;
ConnectionString = connectionString;
}
public static IDisposable Use(DbContextOptionsFactoryContext context)
{
_current.Value = context;
return new DisposeAction(() => _current.Value = null);
}
}
}

22
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs

@ -1,6 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
namespace Volo.Abp.Uow.EntityFrameworkCore namespace Volo.Abp.Uow.EntityFrameworkCore
{ {
@ -26,16 +27,21 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
throw new AbpException("A DbContext can only be created inside a unit of work!"); throw new AbpException("A DbContext can only be created inside a unit of work!");
} }
var connectionString = _connectionStringResolver.Resolve<TDbContext>(); var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
var connectionString = _connectionStringResolver.Resolve(connectionStringName);
var dbContextKey = $"{typeof(TDbContext).FullName}_{connectionString}"; var dbContextKey = $"{typeof(TDbContext).FullName}_{connectionString}";
var databaseApi = unitOfWork.GetOrAddDatabaseApi( using (DbContextOptionsFactoryContext.Use(new DbContextOptionsFactoryContext(connectionStringName, connectionString)))
dbContextKey, {
() => new DbContextDatabaseApi<TDbContext>( var databaseApi = unitOfWork.GetOrAddDatabaseApi(
unitOfWork.ServiceProvider.GetRequiredService<TDbContext>() dbContextKey,
)); () => new DbContextDatabaseApi<TDbContext>(
unitOfWork.ServiceProvider.GetRequiredService<TDbContext>()
return ((DbContextDatabaseApi<TDbContext>)databaseApi).DbContext; ));
return ((DbContextDatabaseApi<TDbContext>)databaseApi).DbContext;
}
} }
} }
} }

12
src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs

@ -9,10 +9,9 @@ namespace Volo.Abp.Uow
{ {
public Guid Id => _parent.Id; public Guid Id => _parent.Id;
public IUnitOfWork Outer public UnitOfWorkStartOptions Options => _parent.Options;
{
get => _parent.Outer; public IUnitOfWork Outer => _parent.Outer;
}
public bool IsReserved public bool IsReserved
{ {
@ -31,6 +30,11 @@ namespace Volo.Abp.Uow
_parent.SetOuter(outer); _parent.SetOuter(outer);
} }
public void SetOptions(UnitOfWorkStartOptions options)
{
_parent.SetOptions(options);
}
public event EventHandler Completed; public event EventHandler Completed;
public event EventHandler<UnitOfWorkFailedEventArgs> Failed; public event EventHandler<UnitOfWorkFailedEventArgs> Failed;
public event EventHandler Disposed; public event EventHandler Disposed;

4
src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs

@ -4,6 +4,8 @@ namespace Volo.Abp.Uow
{ {
public interface IUnitOfWork : IBasicUnitOfWork, IDatabaseApiContainer public interface IUnitOfWork : IBasicUnitOfWork, IDatabaseApiContainer
{ {
UnitOfWorkStartOptions Options { get; }
IUnitOfWork Outer { get; } IUnitOfWork Outer { get; }
bool IsReserved { get; set; } bool IsReserved { get; set; }
@ -11,5 +13,7 @@ namespace Volo.Abp.Uow
string ReservationName { get; set; } string ReservationName { get; set; }
void SetOuter([CanBeNull] IUnitOfWork outer); void SetOuter([CanBeNull] IUnitOfWork outer);
void SetOptions(UnitOfWorkStartOptions options);
} }
} }

4
src/Volo.Abp/Volo/Abp/Uow/IUnitOfWorkManager.cs

@ -10,8 +10,8 @@ namespace Volo.Abp.Uow
[NotNull] [NotNull]
IBasicUnitOfWork Begin([NotNull] UnitOfWorkStartOptions options); IBasicUnitOfWork Begin([NotNull] UnitOfWorkStartOptions options);
void BeginReserved([NotNull] string reservationName); void BeginReserved([NotNull] string reservationName, [NotNull] UnitOfWorkStartOptions options);
bool TryBeginReserved([NotNull] string reservationName); bool TryBeginReserved([NotNull] string reservationName, [NotNull] UnitOfWorkStartOptions options);
} }
} }

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

@ -10,6 +10,8 @@ namespace Volo.Abp.Uow
{ {
public Guid Id { get; } = Guid.NewGuid(); public Guid Id { get; } = Guid.NewGuid();
public UnitOfWorkStartOptions Options { get; private set; }
public IUnitOfWork Outer { get; private set; } public IUnitOfWork Outer { get; private set; }
public bool IsReserved { get; set; } public bool IsReserved { get; set; }
@ -21,6 +23,11 @@ namespace Volo.Abp.Uow
Outer = outer; Outer = outer;
} }
public void SetOptions(UnitOfWorkStartOptions options)
{
Options = options;
}
public event EventHandler Completed; public event EventHandler Completed;
public event EventHandler<UnitOfWorkFailedEventArgs> Failed; public event EventHandler<UnitOfWorkFailedEventArgs> Failed;
public event EventHandler Disposed; public event EventHandler Disposed;

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

@ -1,4 +1,5 @@
using System; using System;
using System.Data;
namespace Volo.Abp.Uow namespace Volo.Abp.Uow
{ {
@ -11,5 +12,47 @@ namespace Volo.Abp.Uow
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)]
public class UnitOfWorkAttribute : Attribute public class UnitOfWorkAttribute : Attribute
{ {
/// <summary>
/// Is this UOW transactional?
/// Uses default value if not supplied.
/// </summary>
public bool? IsTransactional { get; set; }
/// <summary>
/// Timeout of UOW As milliseconds.
/// Uses default value if not supplied.
/// </summary>
public TimeSpan? Timeout { get; set; }
/// <summary>
/// If this UOW is transactional, this option indicated the isolation level of the transaction.
/// Uses default value if not supplied.
/// </summary>
public IsolationLevel? IsolationLevel { get; set; }
/// <summary>
/// Used to prevent starting a unit of work for the method.
/// If there is already a started unit of work, this property is ignored.
/// Default: false.
/// </summary>
public bool IsDisabled { get; set; }
public virtual void SetOptions(UnitOfWorkStartOptions options)
{
if (IsTransactional.HasValue)
{
options.IsTransactional = IsTransactional;
}
if (Timeout.HasValue)
{
options.Timeout = Timeout;
}
if (IsolationLevel.HasValue)
{
options.IsolationLevel = IsolationLevel;
}
}
} }
} }

18
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkHelper.cs

@ -54,6 +54,24 @@ namespace Volo.Abp.Uow
return false; return false;
} }
public static UnitOfWorkAttribute GetUnitOfWorkAttributeOrNull(MethodInfo methodInfo)
{
var attrs = methodInfo.GetCustomAttributes(true).OfType<UnitOfWorkAttribute>().ToArray();
if (attrs.Length > 0)
{
return attrs[0];
}
attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType<UnitOfWorkAttribute>().ToArray();
if (attrs.Length > 0)
{
return attrs[0];
}
return null;
}
private static bool AnyMethodHasUnitOfWorkAttribute(TypeInfo implementationType) private static bool AnyMethodHasUnitOfWorkAttribute(TypeInfo implementationType)
{ {
return implementationType return implementationType

8
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs

@ -35,15 +35,15 @@ namespace Volo.Abp.Uow
return CreateUnitOfWork(options); return CreateUnitOfWork(options);
} }
public void BeginReserved(string reservationName) public void BeginReserved(string reservationName, UnitOfWorkStartOptions options)
{ {
if (!TryBeginReserved(reservationName)) if (!TryBeginReserved(reservationName, options))
{ {
throw new AbpException($"Could not find a reserved unit of work with reservation name: {reservationName}"); throw new AbpException($"Could not find a reserved unit of work with reservation name: {reservationName}");
} }
} }
public bool TryBeginReserved(string reservationName) public bool TryBeginReserved(string reservationName, UnitOfWorkStartOptions options)
{ {
Check.NotNull(reservationName, nameof(reservationName)); Check.NotNull(reservationName, nameof(reservationName));
@ -61,6 +61,7 @@ namespace Volo.Abp.Uow
} }
uow.IsReserved = false; uow.IsReserved = false;
uow.SetOptions(options);
return true; return true;
} }
@ -90,6 +91,7 @@ namespace Volo.Abp.Uow
unitOfWork.SetOuter(outerUow); unitOfWork.SetOuter(outerUow);
unitOfWork.IsReserved = options.ReservationName != null; unitOfWork.IsReserved = options.ReservationName != null;
unitOfWork.ReservationName = options.ReservationName; unitOfWork.ReservationName = options.ReservationName;
unitOfWork.SetOptions(options);
_ambientUnitOfWork.SetUnitOfWork(unitOfWork); _ambientUnitOfWork.SetUnitOfWork(unitOfWork);

16
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManagerExtensions.cs

@ -28,5 +28,21 @@ namespace Volo.Abp.Uow
return unitOfWorkManager.Begin(new UnitOfWorkStartOptions { ReservationName = reservationName }); return unitOfWorkManager.Begin(new UnitOfWorkStartOptions { ReservationName = reservationName });
} }
public static void BeginReserved([NotNull] this IUnitOfWorkManager unitOfWorkManager, [NotNull] string reservationName)
{
Check.NotNull(unitOfWorkManager, nameof(unitOfWorkManager));
Check.NotNull(reservationName, nameof(reservationName));
unitOfWorkManager.BeginReserved(reservationName, new UnitOfWorkStartOptions());
}
public static void TryBeginReserved([NotNull] this IUnitOfWorkManager unitOfWorkManager, [NotNull] string reservationName)
{
Check.NotNull(unitOfWorkManager, nameof(unitOfWorkManager));
Check.NotNull(reservationName, nameof(reservationName));
unitOfWorkManager.TryBeginReserved(reservationName, new UnitOfWorkStartOptions());
}
} }
} }

50
src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkStartOptions.cs

@ -1,9 +1,57 @@
namespace Volo.Abp.Uow using System;
using System.Data;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Uow
{ {
public class UnitOfWorkStartOptions public class UnitOfWorkStartOptions
{ {
//Revise this since Begin/BeginReserved accepts different options and that can make confusion!
public bool RequiresNew { get; set; } public bool RequiresNew { get; set; }
public string ReservationName { get; set; } public string ReservationName { get; set; }
public bool? IsTransactional { get; set; }
public IsolationLevel? IsolationLevel { get; set; }
public TimeSpan? Timeout { get; set; }
}
/// <summary>
/// Global (default) unit of work options
/// </summary>
public class UnitOfWorkOptions
{
public IUnitOfWorkTransactionBehavior IsTransactional { get; set; }
public IsolationLevel? IsolationLevel { get; set; }
}
public interface IUnitOfWorkTransactionBehavior
{
UnitOfWorkStartOptions Set(UnitOfWorkTransactionBehaviorContext context);
}
public class ConventionalUnitOfWorkTransactionBehavior : IUnitOfWorkTransactionBehavior
{
public UnitOfWorkStartOptions Set(UnitOfWorkTransactionBehaviorContext context)
{
throw new NotImplementedException();
}
}
public class UnitOfWorkTransactionBehaviorContext : IServiceProviderAccessor
{
public UnitOfWorkStartOptions Options { get; }
public IServiceProvider ServiceProvider { get; }
public UnitOfWorkTransactionBehaviorContext(UnitOfWorkStartOptions options, IServiceProvider serviceProvider)
{
Options = options;
ServiceProvider = serviceProvider;
}
} }
} }
Loading…
Cancel
Save