Browse Source

Redesigned audit logging.

pull/395/head
Halil ibrahim Kalkan 8 years ago
parent
commit
858f57f7f4
  1. 27
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Builder/AbpAspNetCoreMvcApplicationBuilderExtensions.cs
  2. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  3. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  4. 58
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs
  5. 4
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
  6. 28
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  7. 8
      framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj
  8. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpActionInfoInHttpContext.cs
  9. 24
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs
  10. 33
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpUnitOfWorkMiddleware.cs
  11. 2
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/HttpContextClientInfoProvider.cs
  12. 4
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs
  13. 0
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs
  14. 0
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultHttpExceptionStatusCodeFinder.cs
  15. 0
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/ExceptionHttpStatusCodeOptions.cs
  16. 0
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IExceptionToErrorInfoConverter.cs
  17. 0
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IHttpExceptionStatusCodeFinder.cs
  18. 6
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Uow/AbpUnitOfWorkMiddleware.cs
  19. 1
      framework/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj
  20. 4
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
  21. 55
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditInfo.cs
  22. 17
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditInfoContributionContext.cs
  23. 25
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs
  24. 18
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogContributionContext.cs
  25. 9
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogContributor.cs
  26. 79
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs
  27. 12
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogScope.cs
  28. 10
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditScope.cs
  29. 47
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs
  30. 70
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs
  31. 90
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingManager.cs
  32. 14
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingOptions.cs
  33. 2
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingStoreExtensions.cs
  34. 4
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/ClientInfoAuditLogContributor.cs
  35. 29
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs
  36. 2
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeType.cs
  37. 13
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs
  38. 9
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditInfoContributionContext.cs
  39. 9
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditInfoContributor.cs
  40. 12
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogSaveHandle.cs
  41. 10
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs
  42. 12
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingManager.cs
  43. 2
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingStore.cs
  44. 7
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/SimpleLogAuditingStore.cs
  45. 15
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpDictionaryExtensions.cs
  46. 1
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEntry.cs
  47. 1
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs
  48. 1
      framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpThreadingModule.cs
  49. 86
      framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs
  50. 23
      framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalAmbientDataContext.cs
  51. 9
      framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IAmbientDataContext.cs
  52. 11
      framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IAmbientScopeProvider.cs
  53. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs
  54. 15
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs
  55. 2
      templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs

27
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Builder/AbpAspNetCoreMvcApplicationBuilderExtensions.cs

@ -1,27 +0,0 @@
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.Uow;
namespace Microsoft.AspNetCore.Builder
{
public static class AbpAspNetCoreMvcApplicationBuilderExtensions
{
public static IApplicationBuilder UseUnitOfWork(this IApplicationBuilder app)
{
return app
.UseAbpExceptionHandling()
.UseMiddleware<AbpUnitOfWorkMiddleware>();
}
public static IApplicationBuilder UseAbpExceptionHandling(this IApplicationBuilder app)
{
//Prevent multiple add
if (app.Properties.ContainsKey("_AbpExceptionHandlingMiddleware_Added")) //TODO: Constant
{
return app;
}
app.Properties["_AbpExceptionHandlingMiddleware_Added"] = true;
return app.UseMiddleware<AbpExceptionHandlingMiddleware>();
}
}
}

1
framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -17,7 +17,6 @@
<ProjectReference Include="..\Volo.Abp.ApiVersioning.Abstractions\Volo.Abp.ApiVersioning.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj" />
<ProjectReference Include="..\Volo.Abp.Ddd.Application\Volo.Abp.Ddd.Application.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
<ProjectReference Include="..\Volo.Abp.UI\Volo.Abp.UI.csproj" />
</ItemGroup>

1
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs

@ -30,7 +30,6 @@ namespace Volo.Abp.AspNetCore.Mvc
[DependsOn(typeof(AbpAspNetCoreModule))]
[DependsOn(typeof(AbpLocalizationModule))]
[DependsOn(typeof(AbpApiVersioningAbstractionsModule))]
[DependsOn(typeof(AbpHttpModule))]
[DependsOn(typeof(AbpDddApplicationModule))]
[DependsOn(typeof(AbpUiModule))]
public class AbpAspNetCoreMvcModule : AbpModule

58
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditActionFilter.cs

@ -14,16 +14,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing
{
protected AuditingOptions Options { get; }
private readonly IAuditingHelper _auditingHelper;
private readonly IAuditingManager _auditingManager;
public AbpAuditActionFilter(IOptions<AuditingOptions> options, IAuditingHelper auditingHelper)
public AbpAuditActionFilter(IOptions<AuditingOptions> options, IAuditingHelper auditingHelper, IAuditingManager auditingManager)
{
Options = options.Value;
_auditingHelper = auditingHelper;
_auditingManager = auditingManager;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!ShouldSaveAudit(context))
if (!ShouldSaveAudit(context, out var auditLog, out var auditLogAction))
{
await next();
return;
@ -31,41 +33,65 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing
using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.Auditing))
{
var auditInfo = _auditingHelper.CreateAuditInfo(
context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(),
context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo,
context.ActionArguments
);
var stopwatch = Stopwatch.StartNew();
try
{
var result = await next();
if (result.Exception != null && !result.ExceptionHandled)
{
auditInfo.Exception = result.Exception;
auditLog.Exceptions.Add(result.Exception);
}
}
catch (Exception ex)
{
auditInfo.Exception = ex;
auditLog.Exceptions.Add(ex);
throw;
}
finally
{
stopwatch.Stop();
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
await _auditingHelper.SaveAsync(auditInfo);
auditLogAction.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
auditLog.Actions.Add(auditLogAction);
}
}
}
private bool ShouldSaveAudit(ActionExecutingContext actionContext)
private bool ShouldSaveAudit(ActionExecutingContext context, out AuditLogInfo auditLog, out AuditLogActionInfo auditLogAction)
{
return Options.IsEnabled &&
actionContext.ActionDescriptor.IsControllerAction() &&
_auditingHelper.ShouldSaveAudit(actionContext.ActionDescriptor.GetMethodInfo(), true);
auditLog = null;
auditLogAction = null;
if (!Options.IsEnabled)
{
return false;
}
if (!context.ActionDescriptor.IsControllerAction())
{
return false;
}
var auditLogScope = _auditingManager.Current;
if (auditLogScope == null)
{
return false;
}
if (!_auditingHelper.ShouldSaveAudit(context.ActionDescriptor.GetMethodInfo(), true))
{
return false;
}
auditLog = auditLogScope.Log;
auditLogAction = _auditingHelper.CreateAuditLogAction(
context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(),
context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo,
context.ActionArguments
);
return true;
}
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
@ -11,7 +12,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
{
public class AbpUowActionFilter : IAsyncActionFilter, ITransientDependency
{
public const string UnitOfWorkReservationName = "_AbpActionUnitOfWork";
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly UnitOfWorkDefaultOptions _defaultOptions;
@ -47,7 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
var options = CreateOptions(context, unitOfWorkAttr);
//Trying to begin a reserved UOW by AbpUnitOfWorkMiddleware
if (_unitOfWorkManager.TryBeginReserved(UnitOfWorkReservationName, options))
if (_unitOfWorkManager.TryBeginReserved(AbpUnitOfWorkMiddleware.UnitOfWorkReservationName, options))
{
var result = await next();
if (!Succeed(result))

28
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs

@ -1,6 +1,9 @@
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;
namespace Microsoft.AspNetCore.Builder
@ -14,5 +17,30 @@ namespace Microsoft.AspNetCore.Builder
app.ApplicationServices.GetRequiredService<ObjectAccessor<IApplicationBuilder>>().Value = app;
app.ApplicationServices.GetRequiredService<IAbpApplicationWithExternalServiceProvider>().Initialize(app.ApplicationServices);
}
public static IApplicationBuilder UseAuditing(this IApplicationBuilder app)
{
return app
.UseMiddleware<AbpAuditingMiddleware>();
}
public static IApplicationBuilder UseUnitOfWork(this IApplicationBuilder app)
{
return app
.UseAbpExceptionHandling()
.UseMiddleware<AbpUnitOfWorkMiddleware>();
}
public static IApplicationBuilder UseAbpExceptionHandling(this IApplicationBuilder app)
{
//Prevent multiple add
if (app.Properties.ContainsKey("_AbpExceptionHandlingMiddleware_Added")) //TODO: Constant
{
return app;
}
app.Properties["_AbpExceptionHandlingMiddleware_Added"] = true;
return app.UseMiddleware<AbpExceptionHandlingMiddleware>();
}
}
}

8
framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj

@ -15,8 +15,14 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj" />
<ProjectReference Include="..\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<ProjectReference Include="..\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
<ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
<ProjectReference Include="..\Volo.Abp.UI\Volo.Abp.UI.csproj" />
<ProjectReference Include="..\Volo.Abp.Uow\Volo.Abp.Uow.csproj" />
<ProjectReference Include="..\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
<ProjectReference Include="..\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" />
</ItemGroup>

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpActionInfoInHttpContext.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpActionInfoInHttpContext.cs

@ -1,6 +1,6 @@
namespace Volo.Abp.AspNetCore.Mvc
{
public class AbpActionInfoInHttpContext
public class AbpActionInfoInHttpContext //Rename?
{
public bool IsObjectResult { get; set; }
}

24
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs

@ -1,15 +1,31 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Auditing;
using Volo.Abp.Authorization;
using Volo.Abp.Domain;
using Volo.Abp.Http;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Security;
using Volo.Abp.Threading;
using Volo.Abp.UI;
using Volo.Abp.Uow;
using Volo.Abp.Validation;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.AspNetCore
{
[DependsOn(typeof(AbpThreadingModule))]
[DependsOn(typeof(AbpSecurityModule))]
[DependsOn(typeof(AbpVirtualFileSystemModule))]
[DependsOn(
typeof(AbpAuditingModule),
typeof(AbpSecurityModule),
typeof(AbpVirtualFileSystemModule),
typeof(AbpUnitOfWorkModule),
typeof(AbpHttpModule),
typeof(AbpAuthorizationModule),
typeof(AbpDddDomainModule), //TODO: Can we remove this?
typeof(AbpLocalizationModule),
typeof(AbpUiModule), //TODO: Can we remove this?
typeof(AbpValidationModule)
)]
public class AbpAspNetCoreModule : IAbpModule
{
public void ConfigureServices(ServiceConfigurationContext context)

33
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AbpUnitOfWorkMiddleware.cs

@ -0,0 +1,33 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Volo.Abp.Auditing;
namespace Volo.Abp.AspNetCore.Auditing
{
public class AbpAuditingMiddleware
{
private readonly RequestDelegate _next;
private readonly IAuditingManager _auditingManager;
public AbpAuditingMiddleware(RequestDelegate next, IAuditingManager auditingManager)
{
_next = next;
_auditingManager = auditingManager;
}
public async Task Invoke(HttpContext httpContext)
{
using (var scope = _auditingManager.BeginScope())
{
try
{
await _next(httpContext);
}
finally
{
await scope.SaveAsync();
}
}
}
}
}

2
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Auditing/HttpContextClientInfoProvider.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/HttpContextClientInfoProvider.cs

@ -4,7 +4,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Auditing;
namespace Microsoft.AspNetCore.Auditing
namespace Volo.Abp.AspNetCore.Auditing
{
public class HttpContextClientInfoProvider : IClientInfoProvider
{

4
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionHandlingMiddleware.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs

@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Volo.Abp.AspNetCore.Mvc.Uow;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.Http;
using Volo.Abp.Json;

0
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs

0
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/DefaultHttpExceptionStatusCodeFinder.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultHttpExceptionStatusCodeFinder.cs

0
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionHttpStatusCodeOptions.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/ExceptionHttpStatusCodeOptions.cs

0
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/IExceptionToErrorInfoConverter.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IExceptionToErrorInfoConverter.cs

0
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/IHttpExceptionStatusCodeFinder.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/IHttpExceptionStatusCodeFinder.cs

6
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUnitOfWorkMiddleware.cs → framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Uow/AbpUnitOfWorkMiddleware.cs

@ -2,10 +2,12 @@
using Microsoft.AspNetCore.Http;
using Volo.Abp.Uow;
namespace Volo.Abp.AspNetCore.Mvc.Uow
namespace Volo.Abp.AspNetCore.Uow
{
public class AbpUnitOfWorkMiddleware
{
public const string UnitOfWorkReservationName = "_AbpActionUnitOfWork";
private readonly RequestDelegate _next;
private readonly IUnitOfWorkManager _unitOfWorkManager;
@ -17,7 +19,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
public async Task Invoke(HttpContext httpContext)
{
using (var uow = _unitOfWorkManager.Reserve(AbpUowActionFilter.UnitOfWorkReservationName))
using (var uow = _unitOfWorkManager.Reserve(UnitOfWorkReservationName))
{
await _next(httpContext);
await uow.CompleteAsync(httpContext.RequestAborted);

1
framework/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj

@ -18,6 +18,7 @@
<ProjectReference Include="..\Volo.Abp.Json\Volo.Abp.Json.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy.Abstractions\Volo.Abp.MultiTenancy.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
<ProjectReference Include="..\Volo.Abp.Timing\Volo.Abp.Timing.csproj" />
</ItemGroup>

4
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs

@ -1,16 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
namespace Volo.Abp.Auditing
{
[DependsOn(
typeof(AbpDataModule),
typeof(AbpJsonModule),
typeof(AbpTimingModule),
typeof(AbpSecurityModule),
typeof(AbpThreadingModule),
typeof(AbpMultiTenancyAbstractionsModule)
)]
public class AbpAuditingModule : AbpModule

55
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditInfo.cs

@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Data;
namespace Volo.Abp.Auditing
{
public class AuditInfo : IHasExtraProperties
{
public Guid? TenantId { get; set; }
public Guid? UserId { get; set; }
public Guid? ImpersonatorUserId { get; set; }
public Guid? ImpersonatorTenantId { get; set; }
public string ServiceName { get; set; }
public string MethodName { get; set; }
public string Parameters { get; set; }
public DateTime ExecutionTime { get; set; }
public int ExecutionDuration { get; set; }
public string ClientIpAddress { get; set; }
public string ClientName { get; set; }
public string BrowserInfo { get; set; }
public Exception Exception { get; set; }
public Dictionary<string, object> ExtraProperties { get; }
public AuditInfo()
{
ExtraProperties = new Dictionary<string, object>();
}
public override string ToString()
{
var loggedUserId = UserId.HasValue
? "user " + UserId.Value
: "an anonymous user";
var exceptionOrSuccessMessage = Exception != null
? "exception: " + Exception.Message
: "succeed";
return $"AUDIT LOG: {ServiceName}.{MethodName} is executed by {loggedUserId} in {ExecutionDuration} ms from {ClientIpAddress} IP address with {exceptionOrSuccessMessage}.";
}
}
}

17
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditInfoContributionContext.cs

@ -1,17 +0,0 @@
using System;
namespace Volo.Abp.Auditing
{
public class AuditInfoContributionContext : IAuditInfoContributionContext
{
public IServiceProvider ServiceProvider { get; }
public AuditInfo AuditInfo { get; set; }
public AuditInfoContributionContext(IServiceProvider serviceProvider, AuditInfo auditInfo)
{
ServiceProvider = serviceProvider;
AuditInfo = auditInfo;
}
}
}

25
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogActionInfo.cs

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.Auditing
{
public class AuditLogActionInfo
{
public string ServiceName { get; set; }
public string MethodName { get; set; }
public string Parameters { get; set; }
public DateTime ExecutionTime { get; set; }
public int ExecutionDuration { get; set; }
public Dictionary<string, object> ExtraProperties { get; }
public AuditLogActionInfo()
{
ExtraProperties = new Dictionary<string, object>();
}
}
}

18
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogContributionContext.cs

@ -0,0 +1,18 @@
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Auditing
{
public class AuditLogContributionContext : IServiceProviderAccessor
{
public IServiceProvider ServiceProvider { get; }
public AuditLogInfo AuditInfo { get; }
public AuditLogContributionContext(IServiceProvider serviceProvider, AuditLogInfo auditInfo)
{
ServiceProvider = serviceProvider;
AuditInfo = auditInfo;
}
}
}

9
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogContributor.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.Auditing
{
public abstract class AuditLogContributor
{
public abstract Task ContributeAsync(AuditLogContributionContext context);
}
}

79
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogInfo.cs

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Auditing
{
public class AuditLogInfo : IMultiTenant
{
public Guid? UserId { get; set; }
public Guid? TenantId { get; set; }
public Guid? ImpersonatorUserId { get; set; }
public Guid? ImpersonatorTenantId { get; set; }
public DateTime ExecutionTime { get; set; }
public int ExecutionDuration { get; set; }
public string ClientIpAddress { get; set; }
public string ClientName { get; set; }
public string BrowserInfo { get; set; }
public List<AuditLogActionInfo> Actions { get; set; }
public List<Exception> Exceptions { get; }
public Dictionary<string, object> ExtraProperties { get; }
public IList<EntityChangeInfo> EntityChanges { get; }
public AuditLogInfo()
{
Actions = new List<AuditLogActionInfo>();
Exceptions = new List<Exception>();
ExtraProperties = new Dictionary<string, object>();
EntityChanges = new List<EntityChangeInfo>();
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine("AUDIT LOG:");
sb.AppendLine($"- UserId : {UserId}");
sb.AppendLine($"- ClientIpAddress : {ClientIpAddress}");
sb.AppendLine($"- ExecutionDuration : {ExecutionDuration}");
if (Actions.Any())
{
sb.AppendLine("- Actions:");
foreach (var action in Actions)
{
sb.AppendLine($" - {action.ServiceName}.{action.MethodName} ({action.ExecutionDuration} ms.)");
sb.AppendLine($" - {action.Parameters}");
}
}
if (Exceptions.Any())
{
sb.AppendLine("- Exceptions:");
foreach (var exception in Exceptions)
{
sb.AppendLine($" - {exception.Message}");
sb.AppendLine($" - {exception}");
}
}
//TODO: EntityChanges
return sb.ToString();
}
}
}

12
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditLogScope.cs

@ -0,0 +1,12 @@
namespace Volo.Abp.Auditing
{
public class AuditLogScope : IAuditLogScope
{
public AuditLogInfo Log { get; }
public AuditLogScope(AuditLogInfo log)
{
Log = log;
}
}
}

10
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditScope.cs

@ -0,0 +1,10 @@
using JetBrains.Annotations;
namespace Volo.Abp.Auditing
{
public interface IAuditLogScope
{
[NotNull]
AuditLogInfo Log { get; }
}
}

47
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingHelper.cs

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -89,19 +88,31 @@ namespace Volo.Abp.Auditing
return defaultValue;
}
public virtual AuditInfo CreateAuditInfo(Type type, MethodInfo method, object[] arguments)
public virtual AuditLogInfo CreateAuditLogInfo()
{
return CreateAuditInfo(type, method, CreateArgumentsDictionary(method, arguments));
}
public virtual AuditInfo CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments)
{
var auditInfo = new AuditInfo
var auditInfo = new AuditLogInfo
{
TenantId = CurrentTenant.Id,
UserId = CurrentUser.Id,
//ImpersonatorUserId = AbpSession.ImpersonatorUserId, //TODO: Impersonation system is not available yet!
//ImpersonatorTenantId = AbpSession.ImpersonatorTenantId,
ExecutionTime = Clock.Now
};
ExecuteContributors(auditInfo);
return auditInfo;
}
public virtual AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, object[] arguments)
{
return CreateAuditLogAction(type, method, CreateArgumentsDictionary(method, arguments));
}
public virtual AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, IDictionary<string, object> arguments)
{
var actionInfo = new AuditLogActionInfo
{
ServiceName = type != null
? type.FullName
: "",
@ -110,22 +121,22 @@ namespace Volo.Abp.Auditing
ExecutionTime = Clock.Now
};
ExecuteContributors(auditInfo);
//TODO Execute contributors
return auditInfo;
return actionInfo;
}
protected virtual void ExecuteContributors(AuditInfo auditInfo)
protected virtual void ExecuteContributors(AuditLogInfo auditLogInfo)
{
using (var scope = ServiceProvider.CreateScope())
{
var context = new AuditInfoContributionContext(scope.ServiceProvider, auditInfo);
var context = new AuditLogContributionContext(scope.ServiceProvider, auditLogInfo);
foreach (var contributor in Options.Contributors)
{
try
{
contributor.Contribute(context);
contributor.ContributeAsync(context);
}
catch (Exception ex)
{
@ -135,16 +146,6 @@ namespace Volo.Abp.Auditing
}
}
public virtual void Save(AuditInfo auditInfo)
{
AuditingStore.Save(auditInfo);
}
public virtual async Task SaveAsync(AuditInfo auditInfo)
{
await AuditingStore.SaveAsync(auditInfo);
}
protected virtual string SerializeConvertArguments(IDictionary<string, object> arguments)
{
try

70
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptor.cs

@ -10,28 +10,22 @@ namespace Volo.Abp.Auditing
public class AuditingInterceptor : AbpInterceptor, ITransientDependency
{
private readonly IAuditingHelper _auditingHelper;
private readonly IAuditingManager _auditingManager;
public AuditingInterceptor(IAuditingHelper auditingHelper)
public AuditingInterceptor(IAuditingHelper auditingHelper, IAuditingManager auditingManager)
{
_auditingHelper = auditingHelper;
_auditingManager = auditingManager;
}
public override void Intercept(IAbpMethodInvocation invocation)
{
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing))
if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction))
{
invocation.Proceed();
return;
}
if (!_auditingHelper.ShouldSaveAudit(invocation.Method))
{
invocation.Proceed();
return;
}
var auditInfo = _auditingHelper.CreateAuditInfo(invocation.TargetObject.GetType(), invocation.Method, invocation.Arguments);
var stopwatch = Stopwatch.StartNew();
try
@ -40,35 +34,25 @@ namespace Volo.Abp.Auditing
}
catch (Exception ex)
{
auditInfo.Exception = ex;
auditLog.Exceptions.Add(ex);
throw;
}
finally
{
stopwatch.Stop();
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
_auditingHelper.Save(auditInfo);
auditLogAction.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
auditLog.Actions.Add(auditLogAction);
}
}
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
//Try to reduce duplication with Intercept
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing))
if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction))
{
await invocation.ProceedAsync();
return;
}
if (!_auditingHelper.ShouldSaveAudit(invocation.Method))
{
await invocation.ProceedAsync();
invocation.Proceed();
return;
}
var auditInfo = _auditingHelper.CreateAuditInfo(invocation.TargetObject.GetType(), invocation.Method, invocation.Arguments);
var stopwatch = Stopwatch.StartNew();
try
@ -77,14 +61,44 @@ namespace Volo.Abp.Auditing
}
catch (Exception ex)
{
auditInfo.Exception = ex;
auditLog.Exceptions.Add(ex);
throw;
}
finally
{
stopwatch.Stop();
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
await _auditingHelper.SaveAsync(auditInfo);
auditLogAction.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);
auditLog.Actions.Add(auditLogAction);
}
}
protected virtual bool ShouldIntercept(IAbpMethodInvocation invocation, out AuditLogInfo auditLog, out AuditLogActionInfo auditLogAction)
{
auditLog = null;
auditLogAction = null;
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing))
{
return false;
}
var auditLogScope = _auditingManager.Current;
if (auditLogScope == null)
{
return false;
}
if (!_auditingHelper.ShouldSaveAudit(invocation.Method))
{
return false;
}
auditLog = auditLogScope.Log;
auditLogAction = _auditingHelper.CreateAuditLogAction(
invocation.TargetObject.GetType(), invocation.Method, invocation.Arguments
);
return true;
}
}
}

90
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingManager.cs

@ -0,0 +1,90 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.Auditing
{
public class AuditingManager : IAuditingManager, ITransientDependency
{
private const string AmbientContextKey = "Volo.Abp.Auditing.IAuditLogScope";
private readonly IAmbientScopeProvider<IAuditLogScope> _ambientScopeProvider;
private readonly IAuditingHelper _auditingHelper;
private readonly IAuditingStore _auditingStore;
public AuditingManager(
IAmbientScopeProvider<IAuditLogScope> ambientScopeProvider,
IAuditingHelper auditingHelper,
IAuditingStore auditingStore)
{
_ambientScopeProvider = ambientScopeProvider;
_auditingHelper = auditingHelper;
_auditingStore = auditingStore;
}
public IAuditLogScope Current => _ambientScopeProvider.GetValue(AmbientContextKey);
public IAuditLogSaveHandle BeginScope()
{
var ambientScope = _ambientScopeProvider.BeginScope(
AmbientContextKey,
new AuditLogScope(_auditingHelper.CreateAuditLogInfo())
);
Debug.Assert(Current != null, "Current != null");
var stopWatch = Stopwatch.StartNew();
return new DisposableSaveHandle(_auditingStore, ambientScope, Current.Log, stopWatch);
}
private class DisposableSaveHandle : IAuditLogSaveHandle
{
private readonly IAuditingStore _auditingStore;
private readonly IDisposable _scope;
private readonly AuditLogInfo _auditLog;
private readonly Stopwatch _stopWatch;
private bool _saved;
public DisposableSaveHandle(IAuditingStore auditingStore, IDisposable scope, AuditLogInfo auditLog, Stopwatch stopWatch)
{
_auditingStore = auditingStore;
_scope = scope;
_auditLog = auditLog;
_stopWatch = stopWatch;
}
private void BeforeSave()
{
_stopWatch.Stop();
_saved = true;
_auditLog.ExecutionDuration = Convert.ToInt32(_stopWatch.Elapsed.TotalMilliseconds);
}
public async Task SaveAsync()
{
BeforeSave();
await _auditingStore.SaveAsync(_auditLog);
}
public void Save()
{
BeforeSave();
_auditingStore.Save(_auditLog);
}
public void Dispose()
{
if (!_saved)
{
Save();
}
_scope.Dispose();
}
}
}
}

14
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingOptions.cs

@ -7,11 +7,19 @@ namespace Volo.Abp.Auditing
{
public class AuditingOptions
{
//TODO: Consider to add an option to disable auditing for application service methods?
/// <summary>
/// Default: true.
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// Default: true.
/// </summary>
public bool IsEnabledForAnonymousUsers { get; set; }
public List<IAuditInfoContributor> Contributors { get; }
public List<AuditLogContributor> Contributors { get; }
public List<Type> IgnoredTypes { get; }
@ -20,9 +28,9 @@ namespace Volo.Abp.Auditing
IsEnabled = true;
IsEnabledForAnonymousUsers = true;
Contributors = new List<IAuditInfoContributor>
Contributors = new List<AuditLogContributor>
{
new ClientInfoAuditInfoContributor()
new ClientInfoAuditLogContributor()
};
IgnoredTypes = new List<Type>

2
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingStoreExtensions.cs

@ -4,7 +4,7 @@ namespace Volo.Abp.Auditing
{
public static class AuditingStoreExtensions
{
public static void Save(this IAuditingStore auditingStore, AuditInfo auditInfo)
public static void Save(this IAuditingStore auditingStore, AuditLogInfo auditInfo)
{
AsyncHelper.RunSync(() => auditingStore.SaveAsync(auditInfo));
}

4
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/ClientInfoAuditInfoContributor.cs → framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/ClientInfoAuditLogContributor.cs

@ -4,9 +4,9 @@ using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.Auditing
{
public class ClientInfoAuditInfoContributor : IAuditInfoContributor
public class ClientInfoAuditLogContributor : AuditLogContributor
{
public Task Contribute(IAuditInfoContributionContext context)
public override Task ContributeAsync(AuditLogContributionContext context)
{
var clientInfoProvider = context.ServiceProvider.GetRequiredService<IClientInfoProvider>();

29
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeInfo.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Auditing
{
public class EntityChangeInfo : IMultiTenant
{
public DateTime ChangeTime { get; set; }
public EntityChangeType ChangeType { get; set; }
public string EntityId { get; set; }
public string EntityTypeFullName { get; set; }
public Guid? TenantId { get; set; }
public ICollection<EntityPropertyChangeInfo> PropertyChanges { get; set; }
#region Not mapped
[NotMapped]
public virtual object EntityEntry { get; set; } //TODO: ???
#endregion
}
}

2
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeType.cs → framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityChangeType.cs

@ -1,4 +1,4 @@
namespace Volo.Abp.Domain.Entities.Events
namespace Volo.Abp.Auditing
{
public enum EntityChangeType : byte
{

13
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/EntityPropertyChangeInfo.cs

@ -0,0 +1,13 @@
namespace Volo.Abp.Auditing
{
public class EntityPropertyChangeInfo
{
public virtual string NewValue { get; set; }
public virtual string OriginalValue { get; set; }
public virtual string PropertyName { get; set; }
public virtual string PropertyTypeFullName { get; set; }
}
}

9
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditInfoContributionContext.cs

@ -1,9 +0,0 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Auditing
{
public interface IAuditInfoContributionContext : IServiceProviderAccessor
{
AuditInfo AuditInfo { get; set; }
}
}

9
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditInfoContributor.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.Auditing
{
public interface IAuditInfoContributor
{
Task Contribute(IAuditInfoContributionContext context);
}
}

12
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogSaveHandle.cs

@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.Auditing
{
public interface IAuditLogSaveHandle : IDisposable
{
void Save();
Task SaveAsync();
}
}

10
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingHelper.cs

@ -1,20 +1,18 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
namespace Volo.Abp.Auditing
{
//TODO: Move ShouldSaveAudit and rename to IAuditingFactory
public interface IAuditingHelper
{
bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false);
AuditInfo CreateAuditInfo(Type type, MethodInfo method, object[] arguments);
AuditLogInfo CreateAuditLogInfo();
AuditInfo CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments);
AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, object[] arguments);
void Save(AuditInfo auditInfo);
Task SaveAsync(AuditInfo auditInfo);
AuditLogActionInfo CreateAuditLogAction(Type type, MethodInfo method, IDictionary<string, object> arguments);
}
}

12
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingManager.cs

@ -0,0 +1,12 @@
using JetBrains.Annotations;
namespace Volo.Abp.Auditing
{
public interface IAuditingManager
{
[CanBeNull]
IAuditLogScope Current { get; }
IAuditLogSaveHandle BeginScope();
}
}

2
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingStore.cs

@ -4,6 +4,6 @@ namespace Volo.Abp.Auditing
{
public interface IAuditingStore
{
Task SaveAsync(AuditInfo auditInfo);
Task SaveAsync(AuditLogInfo auditInfo);
}
}

7
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/SimpleLogAuditingStore.cs

@ -15,12 +15,9 @@ namespace Volo.Abp.Auditing
Logger = NullLogger<SimpleLogAuditingStore>.Instance;
}
public Task SaveAsync(AuditInfo auditInfo)
public Task SaveAsync(AuditLogInfo auditInfo)
{
Logger.LogWithLevel(
auditInfo.Exception == null ? LogLevel.Information : LogLevel.Warning,
auditInfo.ToString()
);
Logger.LogInformation(auditInfo.ToString());
return Task.FromResult(0);
}

15
framework/src/Volo.Abp.Core/System/Collections/Generic/AbpDictionaryExtensions.cs

@ -1,3 +1,5 @@
using System.Collections.Concurrent;
namespace System.Collections.Generic
{
/// <summary>
@ -65,6 +67,19 @@ namespace System.Collections.Generic
{
return dictionary.TryGetValue(key, out var obj) ? obj : default;
}
/// <summary>
/// Gets a value from the dictionary with given key. Returns default value if can not find.
/// </summary>
/// <param name="dictionary">Dictionary to check and get</param>
/// <param name="key">Key to find the value</param>
/// <typeparam name="TKey">Type of the key</typeparam>
/// <typeparam name="TValue">Type of the value</typeparam>
/// <returns>Value if found, default if can not found.</returns>
public static TValue GetOrDefault<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> dictionary, TKey key)
{
return dictionary.TryGetValue(key, out var obj) ? obj : default;
}
/// <summary>
/// Gets a value from the dictionary with given key. Returns default value if can not find.

1
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEntry.cs

@ -1,4 +1,5 @@
using System;
using Volo.Abp.Auditing;
namespace Volo.Abp.Domain.Entities.Events
{

1
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.EventBus;

1
framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpThreadingModule.cs

@ -8,6 +8,7 @@ namespace Volo.Abp.Threading
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddSingleton<ICancellationTokenProvider>(NullCancellationTokenProvider.Instance);
context.Services.AddSingleton(typeof(IAmbientScopeProvider<>), typeof(AmbientDataContextAmbientScopeProvider<>));
context.Services.AddAssemblyOf<AbpThreadingModule>();
}

86
framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AmbientDataContextAmbientScopeProvider.cs

@ -0,0 +1,86 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Volo.Abp.Threading
{
public class AmbientDataContextAmbientScopeProvider<T> : IAmbientScopeProvider<T>
{
public ILogger<AmbientDataContextAmbientScopeProvider<T>> Logger { get; set; }
private static readonly ConcurrentDictionary<string, ScopeItem> ScopeDictionary = new ConcurrentDictionary<string, ScopeItem>();
private readonly IAmbientDataContext _dataContext;
public AmbientDataContextAmbientScopeProvider([NotNull] IAmbientDataContext dataContext)
{
Check.NotNull(dataContext, nameof(dataContext));
_dataContext = dataContext;
Logger = NullLogger<AmbientDataContextAmbientScopeProvider<T>>.Instance;
}
public T GetValue(string contextKey)
{
var item = GetCurrentItem(contextKey);
if (item == null)
{
return default;
}
return item.Value;
}
public IDisposable BeginScope(string contextKey, T value)
{
var item = new ScopeItem(value, GetCurrentItem(contextKey));
if (!ScopeDictionary.TryAdd(item.Id, item))
{
throw new AbpException("Can not add item! ScopeDictionary.TryAdd returns false!");
}
_dataContext.SetData(contextKey, item.Id);
return new DisposeAction(() =>
{
ScopeDictionary.TryRemove(item.Id, out item);
if (item.Outer == null)
{
_dataContext.SetData(contextKey, null);
return;
}
_dataContext.SetData(contextKey, item.Outer.Id);
});
}
private ScopeItem GetCurrentItem(string contextKey)
{
var objKey = _dataContext.GetData(contextKey) as string;
return objKey != null ? ScopeDictionary.GetOrDefault(objKey) : null;
}
private class ScopeItem
{
public string Id { get; }
public ScopeItem Outer { get; }
public T Value { get; }
public ScopeItem(T value, ScopeItem outer = null)
{
Id = Guid.NewGuid().ToString();
Value = value;
Outer = outer;
}
}
}
}

23
framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AsyncLocalAmbientDataContext.cs

@ -0,0 +1,23 @@
using System.Collections.Concurrent;
using System.Threading;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Threading
{
public class AsyncLocalAmbientDataContext : IAmbientDataContext, ISingletonDependency
{
private static readonly ConcurrentDictionary<string, AsyncLocal<object>> AsyncLocalDictionary = new ConcurrentDictionary<string, AsyncLocal<object>>();
public void SetData(string key, object value)
{
var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>());
asyncLocal.Value = value;
}
public object GetData(string key)
{
var asyncLocal = AsyncLocalDictionary.GetOrAdd(key, (k) => new AsyncLocal<object>());
return asyncLocal.Value;
}
}
}

9
framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IAmbientDataContext.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.Threading
{
public interface IAmbientDataContext
{
void SetData(string key, object value);
object GetData(string key);
}
}

11
framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IAmbientScopeProvider.cs

@ -0,0 +1,11 @@
using System;
namespace Volo.Abp.Threading
{
public interface IAmbientScopeProvider<T>
{
T GetValue(string contextKey);
IDisposable BeginScope(string contextKey, T value);
}
}

1
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs

@ -56,6 +56,7 @@ namespace Volo.Abp.AspNetCore.Mvc
var app = context.GetApplicationBuilder();
app.UseMiddleware<FakeAuthenticationMiddleware>();
app.UseAuditing();
app.UseUnitOfWork();
app.UseMvcWithDefaultRoute();
}

15
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs

@ -12,10 +12,11 @@ namespace Volo.Abp.Auditing
public class AuditingInterceptor_Tests : AbpIntegratedTest<AuditingInterceptor_Tests.TestModule>
{
private IAuditingStore _auditingStore;
private IAuditingManager _auditingManager;
public AuditingInterceptor_Tests()
{
_auditingManager = GetRequiredService<IAuditingManager>();
}
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
@ -33,11 +34,15 @@ namespace Volo.Abp.Auditing
public async Task Should_Write_AuditLog_For_Classes_That_Implement_IAuditingEnabled()
{
var myAuditedObject1 = GetRequiredService<MyAuditedObject1>();
await myAuditedObject1.DoItAsync(new InputObject {Value1 = "fourty-two", Value2 = 42});
#pragma warning disable 4014
_auditingStore.Received().SaveAsync(Arg.Any<AuditInfo>());
#pragma warning restore 4014
using (_auditingManager.BeginScope())
{
await myAuditedObject1.DoItAsync(new InputObject { Value1 = "fourty-two", Value2 = 42 });
}
#pragma warning disable 4014
_auditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>());
#pragma warning restore 4014
}
[DependsOn(

2
templates/mvc/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs

@ -189,6 +189,8 @@ namespace MyCompanyName.MyProjectName
options.SwaggerEndpoint("/swagger/v1/swagger.json", "MyProjectName API");
});
app.UseAuditing();
app.UseMvc(routes =>
{
routes.MapRoute(

Loading…
Cancel
Save