using Microsoft.Extensions.Logging; namespace zero.Communication; public class InterceptorInstruction where T : ZeroIdEntity, new() { public Guid Guid { get; private set; } public InterceptorRunType Runtype { get; private set; } public T Model { get; private set; } public Type ModelType { get; private set; } public Result Result { get; private set; } protected IZeroContext Context { get; private set; } protected IEnumerable Interceptors { get; private set; } protected IInterceptors InterceptorHandler { get; private set; } protected ILogger Logger { get; private set; } protected Dictionary CachedParameters { get; private set; } = new(); protected Func InterceptorFilter { get; private set; } = x => true; internal InterceptorInstruction(IInterceptors interceptors, IZeroContext context, IEnumerable registrations, ILogger logger, InterceptorRunType runtype, T model) { InterceptorHandler = interceptors; Context = context; Guid = Guid.NewGuid(); Logger = logger; Runtype = runtype; Model = model; ModelType = model.GetType(); Interceptors = registrations.OrderByDescending(x => x.Gravity); } /// /// Custom interceptor filter for this instruction (the CanHandle() method for each interceptor is still activated) /// public void Filter(Func predicate) { InterceptorFilter = predicate; } /// /// Run all interceptors (in order) which can handle the given type. /// Depending on the action any of the following methods on the interceptor is called: Creating(), Updating(), Deleting(). /// If any of the interceptors returns a result the operation is cancelled and this result is returned. /// public async Task Start() { foreach (IInterceptor interceptor in GetInterceptors()) { InterceptorParameters parameters = new() { Context = Context, Store = Context.Store, Properties = new(), Interceptors = InterceptorHandler }; if (!interceptor.CanHandle(parameters, ModelType)) { continue; } Logger.LogDebug("Run interceptor {interceptor} for {type}:{operation}", interceptor.Name, ModelType, Runtype); InterceptorResult result = (await HandleBefore(interceptor, parameters)) ?? new(); result.InterceptorHash = IdGenerator.Create(32); CachedParameters.Add(interceptor.Hash, parameters); // we cancel all further interceptors if a result is available and return this instead if (result.Result != null) { Result = result.Result.ConvertTo(result.Result.Model as T); return false; } // the Continue task will cancel all further interceptors if (!result.Continue) { break; } } return true; } /// /// Run all interceptors (in order) which can handle the given type. /// Depending on the action any of the following methods on the interceptor is called: Created(), Updated(), Deleted(). /// The parameters which are returned from the Start() operation are passed to the methods. /// public async Task Complete() { foreach (IInterceptor interceptor in GetInterceptors()) { await HandleAfter(interceptor, CachedParameters.GetValueOrDefault(interceptor.Hash)); } } protected IEnumerable GetInterceptors() { return Interceptors.Where(InterceptorFilter).OrderByDescending(x => x.Gravity); } /// /// Proxy for handling methods on an interceptor /// protected Task> HandleBefore(IInterceptor interceptor, InterceptorParameters parameters) => Runtype switch { InterceptorRunType.Create => interceptor.Creating(parameters, Model), InterceptorRunType.Update => interceptor.Updating(parameters, Model), InterceptorRunType.Delete => interceptor.Deleting(parameters, Model), _ => throw new NotImplementedException() }; /// /// Proxy for handling methods on an interceptor /// protected Task HandleAfter(IInterceptor interceptor, InterceptorParameters parameters) => Runtype switch { InterceptorRunType.Create => interceptor.Created(parameters, Model), InterceptorRunType.Update => interceptor.Updated(parameters, Model), InterceptorRunType.Delete => interceptor.Deleted(parameters, Model), _ => throw new NotImplementedException() }; }