using FluentValidation; using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using zero.Core.Entities; using zero.Core.Utils; namespace zero.Core.Collections { public class CollectionInterceptorHandler : ICollectionInterceptorHandler { protected ConcurrentBag Interceptors { get; private set; } = new(); protected ILogger Logger { get; set; } public CollectionInterceptorHandler(IEnumerable items, ILogger logger) { Logger = logger; Interceptors = new(); foreach (ICollectionInterceptor item in items) { Interceptors.Add(new() { Interceptor = item, Hash = IdGenerator.Create(), Name = item.GetType().Name, Gravity = item.Gravity }); } } /// public InterceptorInstruction Create(string operationName, TParameters parameters) where T : ZeroEntity where TParameters : CollectionInterceptor.Parameters { InterceptorInstruction instruction = new(parameters); instruction.Operation = operationName; instruction.BeforeOperationHandler = async expression => await HandleBefore(expression, instruction); instruction.AfterOperationHandler = async expression => await HandleAfter(expression, instruction); return instruction; } /// /// Calls all matching interceptors with the specified expression /// internal async Task HandleBefore(Expression>>> expression, InterceptorInstruction instruction) where T : ZeroEntity where TParameters : CollectionInterceptor.Parameters { Func>> func = default; foreach (InterceptorRef interceptorRef in ForType(typeof(T))) { if (func == default) { func = expression.Compile(); } Logger.LogDebug("Run interceptor {interceptor} before operation {operation}", interceptorRef.Name, instruction.Operation); InterceptorResult result = await func(interceptorRef.Interceptor); if (result == default) { result = new(); } result.InterceptorHash = interceptorRef.Hash; instruction.Results.Add(result); if (result.Result != null) { instruction.EntityResult = result.Result; break; } if (result.Prevent) { break; } } } /// /// Calls all matching interceptors with the specified expression /// internal async Task HandleAfter(Expression> expression, InterceptorInstruction instruction) where T : ZeroEntity where TParameters : CollectionInterceptor.Parameters { Func func = default; instruction ??= new(); foreach (InterceptorRef interceptorRef in ForType(typeof(T))) { InterceptorResult beforeResult = instruction?.Results.FirstOrDefault(res => res.InterceptorHash == interceptorRef.Hash); if (func == default) { func = expression.Compile(); } Logger.LogDebug("Run interceptor {interceptor} after operation {operation}", interceptorRef.Name, instruction.Operation); await func(interceptorRef.Interceptor); } } /// /// Get all interceptors for a certain type /// IEnumerable ForType(Type targetType) { return Interceptors.Where(map => map.Interceptor.CanHandle(targetType)).OrderByDescending(x => x.Gravity); // return Interceptors.Where(item => item.Types.Count == 0 || item.Types.Any(type => targetType.IsAssignableFrom(type))); } protected class InterceptorRef { public string Hash { get; set; } public ICollectionInterceptor Interceptor { get; set; } public string Name { get; set; } public int Gravity { get; set; } } } public interface ICollectionInterceptorHandler { /// /// Creates a new interceptor instruction /// InterceptorInstruction Create(string operationName, TParameters parameters) where T : ZeroEntity where TParameters : CollectionInterceptor.Parameters; } }