mirror of https://github.com/abpframework/abp.git
25 changed files with 748 additions and 2 deletions
@ -0,0 +1,55 @@ |
|||
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}."; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)] |
|||
public class AuditedAttribute : Attribute |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Serialization; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditingContractResolver : CamelCasePropertyNamesContractResolver |
|||
{ |
|||
private readonly List<Type> _ignoredTypes; |
|||
|
|||
public AuditingContractResolver(List<Type> ignoredTypes) |
|||
{ |
|||
_ignoredTypes = ignoredTypes; |
|||
} |
|||
|
|||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) |
|||
{ |
|||
JsonProperty property = base.CreateProperty(member, memberSerialization); |
|||
|
|||
if (member.IsDefined(typeof(DisableAuditingAttribute)) || member.IsDefined(typeof(JsonIgnoreAttribute))) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
} |
|||
|
|||
foreach (var ignoredType in _ignoredTypes) |
|||
{ |
|||
if (ignoredType.GetTypeInfo().IsAssignableFrom(property.PropertyType)) |
|||
{ |
|||
property.ShouldSerialize = instance => false; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return property; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,188 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Timing; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditingHelper : IAuditingHelper, ITransientDependency |
|||
{ |
|||
public ILogger<AuditingHelper> Logger { get; set; } |
|||
|
|||
public IAuditingStore AuditingStore { get; set; } |
|||
protected ICurrentUser CurrentUser { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IClock Clock { get; } |
|||
protected IAuditInfoProvider AuditInfoProvider; |
|||
protected AuditingOptions Options; |
|||
protected IAuditSerializer AuditSerializer; |
|||
|
|||
public AuditingHelper( |
|||
IAuditInfoProvider auditInfoProvider, |
|||
IAuditSerializer auditSerializer, |
|||
IOptions<AuditingOptions> options, |
|||
ICurrentUser currentUser, |
|||
ICurrentTenant currentTenant, |
|||
IClock clock) |
|||
{ |
|||
AuditInfoProvider = auditInfoProvider; |
|||
Options = options.Value; |
|||
AuditSerializer = auditSerializer; |
|||
CurrentUser = currentUser; |
|||
CurrentTenant = currentTenant; |
|||
Clock = clock; |
|||
|
|||
Logger = NullLogger<AuditingHelper>.Instance; |
|||
} |
|||
|
|||
public bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false) |
|||
{ |
|||
if (!Options.IsEnabled) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (!Options.IsEnabledForAnonymousUsers && !CurrentUser.IsAuthenticated) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (methodInfo == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (!methodInfo.IsPublic) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (methodInfo.IsDefined(typeof(AuditedAttribute), true)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (methodInfo.IsDefined(typeof(DisableAuditingAttribute), true)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
var classType = methodInfo.DeclaringType; |
|||
if (classType != null) |
|||
{ |
|||
if (classType.IsDefined(typeof(AuditedAttribute), true)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (classType.IsDefined(typeof(DisableAuditingAttribute), true)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (typeof(IAuditingEnabled).IsAssignableFrom(classType)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return defaultValue; |
|||
} |
|||
|
|||
public AuditInfo CreateAuditInfo(Type type, MethodInfo method, object[] arguments) |
|||
{ |
|||
return CreateAuditInfo(type, method, CreateArgumentsDictionary(method, arguments)); |
|||
} |
|||
|
|||
public AuditInfo CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments) |
|||
{ |
|||
var auditInfo = new AuditInfo |
|||
{ |
|||
TenantId = CurrentTenant.Id, |
|||
UserId = CurrentUser.Id, |
|||
//ImpersonatorUserId = AbpSession.ImpersonatorUserId, //TODO: Impersonation system is not available yet!
|
|||
//ImpersonatorTenantId = AbpSession.ImpersonatorTenantId,
|
|||
ServiceName = type != null |
|||
? type.FullName |
|||
: "", |
|||
MethodName = method.Name, |
|||
Parameters = SerializeConvertArguments(arguments), |
|||
ExecutionTime = Clock.Now |
|||
}; |
|||
|
|||
try |
|||
{ |
|||
AuditInfoProvider.Fill(auditInfo); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogException(ex, LogLevel.Warning); |
|||
} |
|||
|
|||
return auditInfo; |
|||
} |
|||
|
|||
public void Save(AuditInfo auditInfo) |
|||
{ |
|||
AuditingStore.Save(auditInfo); |
|||
} |
|||
|
|||
public async Task SaveAsync(AuditInfo auditInfo) |
|||
{ |
|||
await AuditingStore.SaveAsync(auditInfo); |
|||
} |
|||
|
|||
private string SerializeConvertArguments(IDictionary<string, object> arguments) |
|||
{ |
|||
try |
|||
{ |
|||
if (arguments.IsNullOrEmpty()) |
|||
{ |
|||
return "{}"; |
|||
} |
|||
|
|||
var dictionary = new Dictionary<string, object>(); |
|||
|
|||
foreach (var argument in arguments) |
|||
{ |
|||
if (argument.Value != null && Options.IgnoredTypes.Any(t => t.IsInstanceOfType(argument.Value))) |
|||
{ |
|||
dictionary[argument.Key] = null; |
|||
} |
|||
else |
|||
{ |
|||
dictionary[argument.Key] = argument.Value; |
|||
} |
|||
} |
|||
|
|||
return AuditSerializer.Serialize(dictionary); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogException(ex, LogLevel.Warning); |
|||
return "{}"; |
|||
} |
|||
} |
|||
|
|||
private static Dictionary<string, object> CreateArgumentsDictionary(MethodInfo method, object[] arguments) |
|||
{ |
|||
var parameters = method.GetParameters(); |
|||
var dictionary = new Dictionary<string, object>(); |
|||
|
|||
for (var i = 0; i < parameters.Length; i++) |
|||
{ |
|||
dictionary[parameters[i].Name] = arguments[i]; |
|||
} |
|||
|
|||
return dictionary; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Aspects; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.DynamicProxy; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditingInterceptor : AbpInterceptor, ITransientDependency |
|||
{ |
|||
private readonly IAuditingHelper _auditingHelper; |
|||
|
|||
public AuditingInterceptor(IAuditingHelper auditingHelper) |
|||
{ |
|||
_auditingHelper = auditingHelper; |
|||
} |
|||
|
|||
public override void Intercept(IAbpMethodInvocation invocation) |
|||
{ |
|||
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing)) |
|||
{ |
|||
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 |
|||
{ |
|||
invocation.Proceed(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
auditInfo.Exception = ex; |
|||
throw; |
|||
} |
|||
finally |
|||
{ |
|||
stopwatch.Stop(); |
|||
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds); |
|||
_auditingHelper.Save(auditInfo); |
|||
} |
|||
} |
|||
|
|||
public override async Task InterceptAsync(IAbpMethodInvocation invocation) |
|||
{ |
|||
//Try to reduce duplication with Intercept
|
|||
|
|||
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing)) |
|||
{ |
|||
await invocation.ProceedAsync(); |
|||
return; |
|||
} |
|||
|
|||
if (!_auditingHelper.ShouldSaveAudit(invocation.Method)) |
|||
{ |
|||
await invocation.ProceedAsync(); |
|||
return; |
|||
} |
|||
|
|||
var auditInfo = _auditingHelper.CreateAuditInfo(invocation.TargetObject.GetType(), invocation.Method, invocation.Arguments); |
|||
|
|||
var stopwatch = Stopwatch.StartNew(); |
|||
|
|||
try |
|||
{ |
|||
await invocation.ProceedAsync(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
auditInfo.Exception = ex; |
|||
} |
|||
finally |
|||
{ |
|||
stopwatch.Stop(); |
|||
auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds); |
|||
await _auditingHelper.SaveAsync(auditInfo); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public static class AuditingInterceptorRegistrar |
|||
{ |
|||
public static void RegisterIfNeeded(IOnServiceRegistredContext context) |
|||
{ |
|||
if (ShouldIntercept(context.ImplementationType)) |
|||
{ |
|||
context.Interceptors.TryAdd<AuditingInterceptor>(); |
|||
} |
|||
} |
|||
|
|||
private static bool ShouldIntercept(Type type) |
|||
{ |
|||
if (type.IsDefined(typeof(AuditedAttribute), true)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (type.IsDefined(typeof(DisableAuditingAttribute), true)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (typeof(IAuditingEnabled).IsAssignableFrom(type)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (type.GetMethods().Any(m => m.IsDefined(typeof(AuditedAttribute), true))) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditingOptions |
|||
{ |
|||
public bool IsEnabled { get; set; } |
|||
|
|||
public bool IsEnabledForAnonymousUsers { get; set; } |
|||
|
|||
public List<Type> IgnoredTypes { get; } |
|||
|
|||
public AuditingOptions() |
|||
{ |
|||
IsEnabled = true; |
|||
IsEnabledForAnonymousUsers = true; |
|||
IgnoredTypes = new List<Type>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public static class AuditingStoreExtensions |
|||
{ |
|||
public static void Save(this IAuditingStore auditingStore, AuditInfo auditInfo) |
|||
{ |
|||
AsyncHelper.RunSync(() => auditingStore.SaveAsync(auditInfo)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
//TODO: Implement as multiple providers can contribute!
|
|||
|
|||
/// <summary>
|
|||
/// Default implementation of <see cref="IAuditInfoProvider" />.
|
|||
/// </summary>
|
|||
public class DefaultAuditInfoProvider : IAuditInfoProvider, ITransientDependency |
|||
{ |
|||
protected IClientInfoProvider ClientInfoProvider { get; } |
|||
|
|||
public DefaultAuditInfoProvider(IClientInfoProvider clientInfoProvider) |
|||
{ |
|||
ClientInfoProvider = clientInfoProvider; |
|||
} |
|||
|
|||
public virtual void Fill(AuditInfo auditInfo) |
|||
{ |
|||
if (auditInfo.ClientIpAddress.IsNullOrEmpty()) |
|||
{ |
|||
auditInfo.ClientIpAddress = ClientInfoProvider.ClientIpAddress; |
|||
} |
|||
|
|||
if (auditInfo.BrowserInfo.IsNullOrEmpty()) |
|||
{ |
|||
auditInfo.BrowserInfo = ClientInfoProvider.BrowserInfo; |
|||
} |
|||
|
|||
if (auditInfo.ClientName.IsNullOrEmpty()) |
|||
{ |
|||
auditInfo.ClientName = ClientInfoProvider.ComputerName; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)] |
|||
public class DisableAuditingAttribute : Attribute |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
/// <summary>
|
|||
/// Provides an interface to provide audit informations in the upper layers.
|
|||
/// </summary>
|
|||
public interface IAuditInfoProvider |
|||
{ |
|||
/// <summary>
|
|||
/// Called to fill needed properties.
|
|||
/// </summary>
|
|||
/// <param name="auditInfo">Audit info that is partially filled</param>
|
|||
void Fill(AuditInfo auditInfo); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditSerializer |
|||
{ |
|||
string Serialize(object obj); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditingEnabled |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditingHelper |
|||
{ |
|||
bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false); |
|||
|
|||
AuditInfo CreateAuditInfo(Type type, MethodInfo method, object[] arguments); |
|||
|
|||
AuditInfo CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments); |
|||
|
|||
void Save(AuditInfo auditInfo); |
|||
|
|||
Task SaveAsync(AuditInfo auditInfo); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IAuditingStore |
|||
{ |
|||
/// <summary>
|
|||
/// Should save audits to a persistent store.
|
|||
/// </summary>
|
|||
/// <param name="auditInfo">Audit informations</param>
|
|||
Task SaveAsync(AuditInfo auditInfo); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public interface IClientInfoProvider |
|||
{ |
|||
string BrowserInfo { get; } |
|||
|
|||
string ClientIpAddress { get; } |
|||
|
|||
string ComputerName { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Newtonsoft.Json; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class JsonNetAuditSerializer : IAuditSerializer, ITransientDependency |
|||
{ |
|||
protected AuditingOptions Options; |
|||
|
|||
public JsonNetAuditSerializer(IOptions<AuditingOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public string Serialize(object obj) |
|||
{ |
|||
var options = new JsonSerializerSettings |
|||
{ |
|||
ContractResolver = new AuditingContractResolver(Options.IgnoredTypes) |
|||
}; |
|||
|
|||
return JsonConvert.SerializeObject(obj, options); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
//TODO: Implement on aspnet core layer
|
|||
public class NullClientInfoProvider : IClientInfoProvider, ISingletonDependency |
|||
{ |
|||
public static NullClientInfoProvider Instance { get; } = new NullClientInfoProvider(); |
|||
|
|||
public string BrowserInfo => null; |
|||
public string ClientIpAddress => null; |
|||
public string ComputerName => null; |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
[Dependency(TryRegister = true)] |
|||
public class SimpleLogAuditingStore : IAuditingStore, ISingletonDependency |
|||
{ |
|||
public ILogger<SimpleLogAuditingStore> Logger { get; set; } |
|||
|
|||
public SimpleLogAuditingStore() |
|||
{ |
|||
Logger = NullLogger<SimpleLogAuditingStore>.Instance; |
|||
} |
|||
|
|||
public Task SaveAsync(AuditInfo auditInfo) |
|||
{ |
|||
Logger.LogWithLevel( |
|||
auditInfo.Exception == null ? LogLevel.Information : LogLevel.Warning, |
|||
auditInfo.ToString() |
|||
); |
|||
|
|||
return Task.FromResult(0); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using NSubstitute; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Auditing |
|||
{ |
|||
public class AuditingInterceptor_Tests : AbpIntegratedTest<AuditingInterceptor_Tests.TestModule> |
|||
{ |
|||
private IAuditingStore _auditingStore; |
|||
|
|||
public AuditingInterceptor_Tests() |
|||
{ |
|||
|
|||
} |
|||
|
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
|
|||
protected override void AfterAddApplication(IServiceCollection services) |
|||
{ |
|||
_auditingStore = Substitute.For<IAuditingStore>(); |
|||
services.Replace(ServiceDescriptor.Singleton(_auditingStore)); |
|||
} |
|||
|
|||
[Fact] |
|||
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
|
|||
} |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAuditingModule), |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class TestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddType<MyAuditedObject1>(); |
|||
} |
|||
} |
|||
|
|||
public interface IMyAuditedObject : ITransientDependency, IAuditingEnabled |
|||
{ |
|||
|
|||
} |
|||
|
|||
public class MyAuditedObject1 : IMyAuditedObject |
|||
{ |
|||
public async virtual Task<ResultObject> DoItAsync(InputObject inputObject) |
|||
{ |
|||
return new ResultObject |
|||
{ |
|||
Value1 = inputObject.Value1 + "-result", |
|||
Value2 = inputObject.Value2 + 1 |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public class ResultObject |
|||
{ |
|||
public string Value1 { get; set; } |
|||
|
|||
public int Value2 { get; set; } |
|||
} |
|||
|
|||
public class InputObject |
|||
{ |
|||
public string Value1 { get; set; } |
|||
|
|||
public int Value2 { get; set; } |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue