Skip to content

Commit

Permalink
Resolved #416: implemented inner unit of work scope. Fixed #516: Audi…
Browse files Browse the repository at this point in the history
…t log saving on exception.
  • Loading branch information
hikalkan committed Aug 24, 2015
1 parent 9e23acc commit 56a13bd
Show file tree
Hide file tree
Showing 16 changed files with 312 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ protected override void BeginUow()
}

_transaction = new TransactionScope(
TransactionScopeOption.Required,
Options.Scope.GetValueOrDefault(TransactionScopeOption.Required),
transactionOptions,
Options.AsyncFlowOption.GetValueOrDefault(TransactionScopeAsyncFlowOption.Enabled)
);
Expand Down
56 changes: 24 additions & 32 deletions src/Abp/Auditing/AuditingInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Transactions;
using Abp.Collections.Extensions;
using Abp.Domain.Uow;
using Abp.Json;
using Abp.Runtime.Session;
using Abp.Threading;
Expand All @@ -23,11 +25,13 @@ internal class AuditingInterceptor : IInterceptor
private readonly IAuditingConfiguration _configuration;

private readonly IAuditInfoProvider _auditInfoProvider;
private readonly IUnitOfWorkManager _unitOfWorkManager;

public AuditingInterceptor(IAuditingConfiguration configuration, IAuditInfoProvider auditInfoProvider)
public AuditingInterceptor(IAuditingConfiguration configuration, IAuditInfoProvider auditInfoProvider, IUnitOfWorkManager unitOfWorkManager)
{
_configuration = configuration;
_auditInfoProvider = auditInfoProvider;
_unitOfWorkManager = unitOfWorkManager;

AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
Expand Down Expand Up @@ -101,44 +105,19 @@ private void PerformAsyncAuditing(IInvocation invocation, AuditInfo auditInfo)

if (invocation.Method.ReturnType == typeof(Task))
{
invocation.ReturnValue = InternalAsyncHelper.WaitTaskAndAction(
invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithFinally(
(Task) invocation.ReturnValue,
async () =>
{
stopwatch.Stop();
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
await AuditingStore.SaveAsync(auditInfo);
});
exception => SaveAuditInfo(auditInfo, stopwatch, exception)
);
}
else //Task<TResult>
{
invocation.ReturnValue = InternalAsyncHelper.CallReturnGenericTaskAfterAction(
invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithFinallyAndGetResult(
invocation.Method.ReturnType.GenericTypeArguments[0],
invocation.ReturnValue,
async () =>
{
stopwatch.Stop();
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
await AuditingStore.SaveAsync(auditInfo);
},
() => { }); //TODO: Make an overload that not receives finally action!
exception => SaveAuditInfo(auditInfo, stopwatch, exception)
);
}

//TODO: Handle exceptions!
//try
//{
//}
//catch (Exception ex)
//{
// auditInfo.Exception = ex;
// throw;
//}
//finally
//{
// stopwatch.Stop();
// auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
// AuditingStore.Save(auditInfo);
//}
}

private string ConvertArgumentsToJson(IInvocation invocation)
Expand Down Expand Up @@ -168,5 +147,18 @@ private string ConvertArgumentsToJson(IInvocation invocation)
return "{}";
}
}

private void SaveAuditInfo(AuditInfo auditInfo, Stopwatch stopwatch, Exception exception)
{
stopwatch.Stop();
auditInfo.Exception = exception;
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);

using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.Suppress))
{
AuditingStore.Save(auditInfo);
uow.Complete();
}
}
}
}
20 changes: 0 additions & 20 deletions src/Abp/Authorization/Interceptors/AuthorizationInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,6 @@ private void InterceptAsync(IInvocation invocation, IEnumerable<AbpAuthorizeAttr
}

//TODO: Async is not worked here, we will check it later. For now, using sync authorization.

//var authorizationAttributeHelper = _iocResolver.ResolveAsDisposable<IAuthorizeAttributeHelper>();

//if (invocation.Method.ReturnType == typeof (Task))
//{
// invocation.ReturnValue = InternalAsyncHelper.InvokeWithPreAndFinalActionAsync(
// invocation,
// async () => await authorizationAttributeHelper.Object.AuthorizeAsync(authorizeAttributes),
// () => _iocResolver.Release(authorizationAttributeHelper)
// );
//}
//else
//{
// invocation.ReturnValue = InternalAsyncHelper.CallInvokeWithPreAndFinalActionAsync(
// invocation.Method.ReturnType.GenericTypeArguments[0],
// invocation,
// async () => await authorizationAttributeHelper.Object.AuthorizeAsync(authorizeAttributes),
// () => _iocResolver.Release(authorizationAttributeHelper)
// );
//}
}

private void InterceptSync(IInvocation invocation, IEnumerable<AbpAuthorizeAttribute> authorizeAttributes)
Expand Down
146 changes: 95 additions & 51 deletions src/Abp/Domain/Uow/CallContextCurrentUnitOfWorkProvider.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Runtime.Remoting.Messaging;
using Abp.Dependency;
using Castle.Core;
using Castle.Core.Logging;

namespace Abp.Domain.Uow
{
Expand All @@ -12,82 +12,126 @@ namespace Abp.Domain.Uow
/// </summary>
public class CallContextCurrentUnitOfWorkProvider : ICurrentUnitOfWorkProvider, ITransientDependency
{
public ILogger Logger { get; set; }

private const string ContextKey = "Abp.UnitOfWork.Current";

//TODO: Clear periodically..?
private static readonly ConcurrentDictionary<string, IUnitOfWork> UnitOfWorkDictionary
= new ConcurrentDictionary<string, IUnitOfWork>();
private static readonly ConcurrentDictionary<string, IUnitOfWork> UnitOfWorkDictionary = new ConcurrentDictionary<string, IUnitOfWork>();

public CallContextCurrentUnitOfWorkProvider()
{
Logger = NullLogger.Instance;
}

internal static IUnitOfWork StaticUow
private static IUnitOfWork GetCurrentUow(ILogger logger)
{
get
var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string;
if (unitOfWorkKey == null)
{
var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string;
if (unitOfWorkKey == null)
{
return null;
}
return null;
}

IUnitOfWork unitOfWork;
if (!UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out unitOfWork))
{
CallContext.LogicalSetData(ContextKey, null);
return null;
}
IUnitOfWork unitOfWork;
if (!UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out unitOfWork))
{
logger.Warn("There is a unitOfWorkKey in CallContext but not in UnitOfWorkDictionary!");
CallContext.FreeNamedDataSlot(ContextKey);
return null;
}

if (unitOfWork.IsDisposed)
{
CallContext.LogicalSetData(ContextKey, null);
UnitOfWorkDictionary.TryRemove(unitOfWorkKey, out unitOfWork);
return null;
}
if (unitOfWork.IsDisposed)
{
logger.Warn("There is a unitOfWorkKey in CallContext but the UOW was disposed!");
UnitOfWorkDictionary.TryRemove(unitOfWorkKey, out unitOfWork);
CallContext.FreeNamedDataSlot(ContextKey);
return null;
}

return unitOfWork;
}

return unitOfWork;
private static void SetCurrentUow(IUnitOfWork value, ILogger logger)
{
if (value == null)
{
ExitFromCurrentUowScope(logger);
return;
}

set
var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string;
if (unitOfWorkKey != null)
{
var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string;
if (unitOfWorkKey != null)
IUnitOfWork outer;
if (UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out outer))
{
IUnitOfWork unitOfWork;
if (UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out unitOfWork))
if (outer == value)
{
if (unitOfWork == value)
{
//Setting same object, no need to set again
return;
}

UnitOfWorkDictionary.TryRemove(unitOfWorkKey, out unitOfWork);
logger.Warn("Setting the same UOW to the CallContext, no need to set again!");
return;
}

CallContext.LogicalSetData(ContextKey, null);
value.Outer = outer;
}
}

if (value == null)
{
//It's already null (because of the logic above), no need to set
return;
}
unitOfWorkKey = value.Id;
if (!UnitOfWorkDictionary.TryAdd(unitOfWorkKey, value))
{
throw new AbpException("Can not set unit of work! UnitOfWorkDictionary.TryAdd returns false!");
}

unitOfWorkKey = Guid.NewGuid().ToString();
if (!UnitOfWorkDictionary.TryAdd(unitOfWorkKey, value))
{
//This is almost impossible, but we're checking.
throw new AbpException("Can not set unit of work!");
}
logger.Debug("Entering a new UOW scope: " + unitOfWorkKey);
CallContext.LogicalSetData(ContextKey, unitOfWorkKey);
}

private static void ExitFromCurrentUowScope(ILogger logger)
{
var unitOfWorkKey = CallContext.LogicalGetData(ContextKey) as string;

CallContext.LogicalSetData(ContextKey, unitOfWorkKey);
if (unitOfWorkKey == null)
{
logger.Warn("There is no current UOW to exit!");
return;
}

IUnitOfWork unitOfWork;
if (!UnitOfWorkDictionary.TryGetValue(unitOfWorkKey, out unitOfWork))
{
logger.Warn("There is a unitOfWorkKey in CallContext but not in UnitOfWorkDictionary!");
CallContext.FreeNamedDataSlot(ContextKey);
return;
}

UnitOfWorkDictionary.TryRemove(unitOfWorkKey, out unitOfWork);
if (unitOfWork.Outer == null)
{
logger.Debug("Exiting from UOW: " + unitOfWorkKey + " (no outer)");
CallContext.FreeNamedDataSlot(ContextKey);
return;
}

//Restore outer UOW

var outerUnitOfWorkKey = unitOfWork.Outer.Id;
if (!UnitOfWorkDictionary.TryGetValue(outerUnitOfWorkKey, out unitOfWork))
{
//No outer UOW
logger.Warn("Outer UOW key could not found in UnitOfWorkDictionary!");
CallContext.FreeNamedDataSlot(ContextKey);
return;
}

logger.Debug("Exiting from UOW: " + unitOfWorkKey + " and returned to outer UOW: " + outerUnitOfWorkKey);
CallContext.LogicalSetData(ContextKey, outerUnitOfWorkKey);
}

/// <inheritdoc />
[DoNotWire]
public IUnitOfWork Current
{
get { return StaticUow; }
set { StaticUow = value; }
get { return GetCurrentUow(Logger); }
set { SetCurrentUow(value, Logger); }
}
}
}
12 changes: 12 additions & 0 deletions src/Abp/Domain/Uow/IUnitOfWork.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;

namespace Abp.Domain.Uow
{
/// <summary>
Expand All @@ -7,6 +9,16 @@ namespace Abp.Domain.Uow
/// </summary>
public interface IUnitOfWork : IActiveUnitOfWork, IUnitOfWorkCompleteHandle
{
/// <summary>
/// Unique id of this UOW.
/// </summary>
string Id { get; }

/// <summary>
/// Reference to the outer UOW if exists.
/// </summary>
IUnitOfWork Outer { get; set; }

/// <summary>
/// Begins the unit of work with given options.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Abp/Domain/Uow/IUnitOfWorkDefaultOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ namespace Abp.Domain.Uow
/// </summary>
public interface IUnitOfWorkDefaultOptions
{
/// <summary>
/// Scope option.
/// </summary>
TransactionScopeOption Scope { get; set; }

/// <summary>
/// Should unit of works be transactional.
/// Default: true.
Expand Down
10 changes: 9 additions & 1 deletion src/Abp/Domain/Uow/IUnitOfWorkManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Abp.Domain.Uow
using System.Transactions;

namespace Abp.Domain.Uow
{
/// <summary>
/// Unit of work manager.
Expand All @@ -17,6 +19,12 @@ public interface IUnitOfWorkManager
/// <returns>A handle to be able to complete the unit of work</returns>
IUnitOfWorkCompleteHandle Begin();

/// <summary>
/// Begins a new unit of work.
/// </summary>
/// <returns>A handle to be able to complete the unit of work</returns>
IUnitOfWorkCompleteHandle Begin(TransactionScopeOption scope);

/// <summary>
/// Begins a new unit of work.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Abp/Domain/Uow/UnitOfWorkBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ namespace Abp.Domain.Uow
/// </summary>
public abstract class UnitOfWorkBase : IUnitOfWork
{
public string Id { get; private set; }

public IUnitOfWork Outer { get; set; }

/// <inheritdoc/>
public event EventHandler Completed;

Expand Down Expand Up @@ -68,6 +72,7 @@ public IReadOnlyList<DataFilterConfiguration> Filters
/// </summary>
protected UnitOfWorkBase(IUnitOfWorkDefaultOptions defaultOptions)
{
Id = Guid.NewGuid().ToString("N");
_filters = defaultOptions.Filters.ToList();
AbpSession = NullAbpSession.Instance;
}
Expand Down

0 comments on commit 56a13bd

Please sign in to comment.