Files
mixtape/zero.Core/Collections/Interceptors/CollectionInterceptorHandler.cs
T

177 lines
5.4 KiB
C#
Raw Normal View History

2020-11-18 15:55:18 +01:00
using FluentValidation;
2021-08-26 13:39:30 +02:00
using Microsoft.Extensions.Logging;
2020-11-18 15:55:18 +01:00
using System;
2021-08-26 13:39:30 +02:00
using System.Collections.Concurrent;
2020-11-18 15:55:18 +01:00
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using zero.Core.Entities;
using zero.Core.Options;
2020-11-18 15:55:18 +01:00
2020-11-19 00:14:52 +01:00
namespace zero.Core.Collections
2020-11-18 15:55:18 +01:00
{
2020-11-19 00:14:52 +01:00
public class CollectionInterceptorHandler : ICollectionInterceptorHandler
2020-11-18 15:55:18 +01:00
{
protected ConcurrentDictionary<Type, object> Interceptors { get; private set; } = new();
protected IZeroOptions Options { get; set; }
protected IServiceProvider Services { get; set; }
2021-08-26 13:39:30 +02:00
protected ILogger<ICollectionInterceptorHandler> Logger { get; set; }
2020-11-18 15:55:18 +01:00
public CollectionInterceptorHandler(IZeroOptions options, IServiceProvider serviceProvider, ILogger<ICollectionInterceptorHandler> logger)
2020-11-18 15:55:18 +01:00
{
2021-08-26 13:39:30 +02:00
Logger = logger;
Options = options;
Services = serviceProvider;
2020-11-18 15:55:18 +01:00
}
/// <inheritdoc />
2021-08-26 13:39:30 +02:00
public InterceptorInstruction<T, TParameters> Create<T, TParameters>(string operationName, TParameters parameters)
where T : ZeroEntity
where TParameters : CollectionInterceptor<T>.Parameters
2020-11-18 15:55:18 +01:00
{
2021-08-26 13:39:30 +02:00
InterceptorInstruction<T, TParameters> instruction = new(parameters);
instruction.Operation = operationName;
instruction.BeforeOperationHandler = async expression => await HandleBefore(expression, instruction);
instruction.AfterOperationHandler = async expression => await HandleAfter(expression, instruction);
2020-11-18 15:55:18 +01:00
2021-08-26 13:39:30 +02:00
return instruction;
}
/// <summary>
/// Calls all matching interceptors with the specified expression
/// </summary>
internal async Task HandleBefore<T, TParameters>(Expression<Func<ICollectionInterceptor<T>, Task<InterceptorResult<T>>>> expression, InterceptorInstruction<T, TParameters> instruction)
2021-08-26 13:39:30 +02:00
where T : ZeroEntity
where TParameters : CollectionInterceptor<T>.Parameters
2021-08-26 13:39:30 +02:00
{
2021-08-26 14:35:34 +02:00
string typeName = (typeof(T)).Name;
Func<ICollectionInterceptor<T>, Task<InterceptorResult<T>>> func = default;
2021-08-26 13:39:30 +02:00
foreach (InterceptorRegistration registration in ForType(typeof(T)))
2020-11-18 15:55:18 +01:00
{
if (!TryResolve(registration, out ICollectionInterceptor<T> interceptor))
{
continue;
}
2020-11-18 15:55:18 +01:00
if (func == default)
{
2021-08-26 13:39:30 +02:00
func = expression.Compile();
2020-11-18 15:55:18 +01:00
}
2021-08-26 14:35:34 +02:00
Logger.LogDebug("Run interceptor {interceptor} for {type}:{operation}", registration.Name, typeName, instruction.Operation);
2020-11-18 15:55:18 +01:00
InterceptorResult<T> result = await func(interceptor);
2021-08-26 13:39:30 +02:00
if (result == default)
2020-11-18 15:55:18 +01:00
{
2021-08-26 13:39:30 +02:00
result = new();
}
result.InterceptorHash = registration.Hash;
2021-08-26 13:39:30 +02:00
instruction.Results.Add(result);
if (result.Result != null)
{
instruction.EntityResult = result.Result;
break;
}
if (result.Prevent)
{
break;
2020-11-18 15:55:18 +01:00
}
}
}
2021-08-26 13:39:30 +02:00
/// <summary>
/// Calls all matching interceptors with the specified expression
/// </summary>
internal async Task HandleAfter<T, TParameters>(Expression<Func<ICollectionInterceptor<T>, Task>> expression, InterceptorInstruction<T, TParameters> instruction)
2021-08-26 13:39:30 +02:00
where T : ZeroEntity
where TParameters : CollectionInterceptor<T>.Parameters
2020-11-18 15:55:18 +01:00
{
Func<ICollectionInterceptor<T>, Task> func = default;
2021-08-26 13:39:30 +02:00
instruction ??= new();
2020-11-18 15:55:18 +01:00
foreach (InterceptorRegistration registration in ForType(typeof(T)))
2020-11-18 15:55:18 +01:00
{
if (!TryResolve(registration, out ICollectionInterceptor<T> interceptor))
{
continue;
}
InterceptorResult<T> beforeResult = instruction?.Results.FirstOrDefault(res => res.InterceptorHash == registration.Hash);
2021-08-26 13:39:30 +02:00
2020-11-18 15:55:18 +01:00
if (func == default)
{
2021-08-26 13:39:30 +02:00
func = expression.Compile();
2020-11-18 15:55:18 +01:00
}
await func(interceptor);
2020-11-18 15:55:18 +01:00
}
}
/// <summary>
/// Get all interceptors for a certain type
/// </summary>
IOrderedEnumerable<InterceptorRegistration> ForType(Type targetType)
2020-11-18 15:55:18 +01:00
{
return Options.Interceptors.GetAllItems().Where(x => x.CanHandle(targetType)).OrderByDescending(x => x.Gravity);
2021-08-26 13:39:30 +02:00
// return Interceptors.Where(item => item.Types.Count == 0 || item.Types.Any(type => targetType.IsAssignableFrom(type)));
}
/// <summary>
/// Resolves an interceptor from the service provider
/// </summary>
bool TryResolve<T>(InterceptorRegistration registration, out ICollectionInterceptor<T> interceptor) where T : ZeroEntity
2021-08-26 13:39:30 +02:00
{
Type type = registration.InterceptorType;
2021-08-26 13:39:30 +02:00
if (Interceptors.TryGetValue(type, out object interceptorObj))
{
interceptor = interceptorObj as ICollectionInterceptor<T>;
2021-09-14 12:41:55 +02:00
return interceptor != null;
}
2021-08-26 13:39:30 +02:00
2021-10-06 11:47:39 +02:00
object service = Services.GetService(type);
interceptor = service as ICollectionInterceptor<T>;
if (interceptor == null && service != null && service is ICollectionInterceptor)
{
interceptor = new CollectionInterceptorShim<T>(service as ICollectionInterceptor);
}
2021-08-26 13:39:30 +02:00
if (interceptor == null)
{
Logger.LogWarning("Could not resolve interceptor {interceptor}", registration.Name);
}
Interceptors.TryAdd(type, interceptor);
return interceptor != null;
2020-11-18 15:55:18 +01:00
}
}
2020-11-19 00:14:52 +01:00
public interface ICollectionInterceptorHandler
2020-11-18 15:55:18 +01:00
{
/// <summary>
2021-08-26 13:39:30 +02:00
/// Creates a new interceptor instruction
2020-11-18 15:55:18 +01:00
/// </summary>
2021-08-26 13:39:30 +02:00
InterceptorInstruction<T, TParameters> Create<T, TParameters>(string operationName, TParameters parameters)
where T : ZeroEntity
where TParameters : CollectionInterceptor<T>.Parameters;
2020-11-18 15:55:18 +01:00
}
}