Skip to content

Commit

Permalink
AuditDbConverterFactory added (#115)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrii Kaplanovskyi <[email protected]>
  • Loading branch information
andriikaplanovskyi and andrii-kaplanovskyi authored Oct 28, 2023
1 parent b744f3e commit 9f69b3f
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ namespace OneBeyond.Studio.EntityAuditing.Domain;

public interface IAuditBulkWriter
{
Task WriteAsync<TEntity>(TEntity entity, AuditEvent @event, CancellationToken cancellationToken)
where TEntity : class;
Task WriteAsync(object entity, AuditEvent @event, CancellationToken cancellationToken);

Task FlushAsync(CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,21 @@ public AuditBulkDbWriter(
_repository = repository;
}

public async Task WriteAsync<TEntity>(TEntity entity, AuditEvent auditEntityEvent, CancellationToken cancellationToken)
where TEntity : class
public async Task WriteAsync(object entity, AuditEvent auditEntityEvent, CancellationToken cancellationToken)
{
EnsureArg.IsNotNull(entity, nameof(entity));
EnsureArg.IsNotNull(auditEntityEvent, nameof(auditEntityEvent));

var entityType = entity.GetType();
var auditConverterType = typeof(AuditDbConverter<>).GetGenericTypeDefinition().MakeGenericType(entityType);

var auditConverter = _serviceProvider.GetService(auditConverterType);
var auditConverter = (IAuditDbConverter)_serviceProvider.GetService(auditConverterType);
if (auditConverter is not null)
{
var auditEvent = await (auditConverter as AuditDbConverter<TEntity>)!.ConvertAsync(entity, auditEntityEvent, cancellationToken);
//var auditEvent = await (auditConverter as AuditDbConverter<TEntity>)!.ConvertAsync(entity, auditEntityEvent, cancellationToken);
var converAsyncFunc = AuditDbConverterFactory.GetOrCompileConvertFunction(entityType);
var auditEvent = await converAsyncFunc(auditConverter, entity, auditEntityEvent);

if (auditEvent is not null)
{
_auditEvents.Add(auditEvent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

namespace OneBeyond.Studio.EntityAuditing.SqlServer;

public class AuditDbConverter<TEntity>
public interface IAuditDbConverter
{
}

public class AuditDbConverter<TEntity> : IAuditDbConverter
where TEntity : class
{
private readonly IJsonAuditEventSerializer<TEntity> _auditStringBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using OneBeyond.Studio.Crosscuts.Reflection;
using OneBeyond.Studio.EntityAuditing.Domain;

namespace OneBeyond.Studio.EntityAuditing.SqlServer;

internal static class AuditDbConverterFactory
{
private static readonly MethodInfo _auditConverterConvertMethodInfoAsync =
Reflector.MethodFrom(() => ConvertEntityEventAsync<object>(default, default, default))
.GetGenericMethodDefinition();

private static readonly ConcurrentDictionary<Type, Func<IAuditDbConverter, object, AuditEvent, Task<Entities.AuditEvent>>> _auditConverterFuncs
= new();

public static Func<IAuditDbConverter, object, AuditEvent, Task<Entities.AuditEvent>> GetOrCompileConvertFunction(Type entityType)
{
if (!_auditConverterFuncs.TryGetValue(entityType, out var auditConverterFunc))
{
auditConverterFunc = CreateConvertFunc(entityType);
_auditConverterFuncs.AddOrUpdate(entityType, auditConverterFunc, (type, function) => function);
}
return auditConverterFunc;
}

private static Func<IAuditDbConverter, object, AuditEvent, Task<Entities.AuditEvent>> CreateConvertFunc(Type entityType)
{
var methodInfo = _auditConverterConvertMethodInfoAsync.MakeGenericMethod(entityType);

var auditWriterParameter = Expression.Parameter(typeof(IAuditDbConverter), "auditConverter");
var entityParameter = Expression.Parameter(typeof(object), "entity");
var eventParameter = Expression.Parameter(typeof(AuditEvent), "event");

var methodCall = Expression.Call(
methodInfo,
auditWriterParameter,
entityParameter,
eventParameter);

var lambda = Expression.Lambda<Func<IAuditDbConverter, object, AuditEvent, Task<Entities.AuditEvent>>>(
methodCall,
auditWriterParameter,
entityParameter,
eventParameter);

return lambda.Compile();
}

private static Task ConvertEntityEventAsync<TEntity>(
IAuditDbConverter auditConverter,
object entity,
AuditEvent @event)
where TEntity : class
=> ((AuditDbConverter<TEntity>)auditConverter).ConvertAsync((TEntity)entity, @event, CancellationToken.None);
}

0 comments on commit 9f69b3f

Please sign in to comment.