* Added new namespace "Aspects" and types dedicated for aspected handlers processing, including Pre\Post processors

* Added new DescriptorAspectsSet class, used for mamanging and executing aspects of handlers
* Processors can prevent handlers execution using Result.Fault()
* Added field to DescribedHandlerInfo for getting descriptor from which this handler was described
* Removed unused reflection extension methods
This commit is contained in:
2025-08-03 00:28:10 +04:00
parent 18e361322e
commit b14d848537
13 changed files with 203 additions and 11 deletions
@@ -0,0 +1,8 @@
namespace Telegrator.Aspects
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class AfterExecutionAttribute<T> : Attribute where T : IPostProcessor
{
public Type ProcessorType => typeof(T);
}
}
@@ -0,0 +1,8 @@
namespace Telegrator.Aspects
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class BeforeExecutionAttribute<T> : Attribute where T : IPreProcessor
{
public Type ProcessorType => typeof(T);
}
}
+13
View File
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using Telegrator.Handlers;
using Telegrator.Handlers.Components;
namespace Telegrator.Aspects
{
public interface IPostProcessor
{
public Task<Result> AfterExecution(IHandlerContainer container);
}
}
+13
View File
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using Telegrator.Handlers;
using Telegrator.Handlers.Components;
namespace Telegrator.Aspects
{
public interface IPreProcessor
{
public Task<Result> BeforeExecution(IHandlerContainer container);
}
}
@@ -9,10 +9,38 @@ namespace Telegrator.Attributes
/// Provides a type-safe way to associate handler types with specific update types and importance settings.
/// </summary>
/// <typeparam name="T">The type of the update handler that this attribute is applied to.</typeparam>
/// <param name="updateType">The type of update that this handler can process.</param>
/// <param name="importance">The importance level for this handler (default: 0 for unlimited).</param>
public abstract class UpdateHandlerAttribute<T>(UpdateType updateType, int importance = 0)
: UpdateHandlerAttributeBase([typeof(T)], updateType, importance) where T : UpdateHandlerBase
public abstract class UpdateHandlerAttribute<T> : UpdateHandlerAttributeBase where T : UpdateHandlerBase
{
/// <summary>
/// Initializes new instance of <see cref="UpdateHandlerAttribute{T}"/>
/// </summary>
/// <param name="updateType">The type of update that this handler can process.</param>
protected UpdateHandlerAttribute(UpdateType updateType)
: base([typeof(T)], updateType, 0) { }
/// <summary>
/// Initializes new instance of <see cref="UpdateHandlerAttribute{T}"/>
/// </summary>
/// <param name="updateType">The type of update that this handler can process.</param>
/// <param name="importance">The importance level for this handler</param>
protected UpdateHandlerAttribute(UpdateType updateType, int importance)
: base([typeof(T)], updateType, importance) { }
/// <summary>
/// Initializes new instance of <see cref="UpdateHandlerAttribute{T}"/>
/// </summary>
/// <param name="types">Additional suported types.</param>
/// <param name="updateType">The type of update that this handler can process.</param>
protected UpdateHandlerAttribute(Type[] types, UpdateType updateType)
: base([..types, typeof(T)], updateType, 0) { }
/// <summary>
/// Initializes new instance of <see cref="UpdateHandlerAttribute{T}"/>
/// </summary>
/// <param name="types">Additional suported types.</param>
/// <param name="updateType">The type of update that this handler can process.</param>
/// <param name="importance">The importance level for this handler</param>
protected UpdateHandlerAttribute(Type[] types, UpdateType updateType, int importance)
: base([.. types, typeof(T)], updateType, importance) { }
}
}
@@ -12,6 +12,11 @@ namespace Telegrator.MadiatorCore.Descriptors
/// </summary>
public class DescribedHandlerInfo
{
/// <summary>
/// descriptor from that handler was described from
/// </summary>
public readonly HandlerDescriptor From;
/// <summary>
/// The update router associated with this handler.
/// </summary>
@@ -60,13 +65,15 @@ namespace Telegrator.MadiatorCore.Descriptors
/// <summary>
/// Initializes a new instance of the <see cref="DescribedHandlerInfo"/> class.
/// </summary>
/// <param name="fromDescriptor">descriptor from that handler was described from</param>
/// <param name="updateRouter">The update router.</param>
/// <param name="client">The Telegram bot client.</param>
/// <param name="handlerInstance">The handler instance.</param>
/// <param name="filterContext">The filter execution context.</param>
/// <param name="displayString">Optional display string.</param>
public DescribedHandlerInfo(IUpdateRouter updateRouter, ITelegramBotClient client, UpdateHandlerBase handlerInstance, FilterExecutionContext<Update> filterContext, string? displayString)
public DescribedHandlerInfo(HandlerDescriptor fromDescriptor, IUpdateRouter updateRouter, ITelegramBotClient client, UpdateHandlerBase handlerInstance, FilterExecutionContext<Update> filterContext, string? displayString)
{
From = fromDescriptor;
UpdateRouter = updateRouter;
Client = client;
HandlerInstance = handlerInstance;
@@ -90,7 +97,26 @@ namespace Telegrator.MadiatorCore.Descriptors
try
{
HandlerContainer = GetContainer(UpdateRouter.AwaitingProvider, this);
return await HandlerInstance.Execute(HandlerContainer, cancellationToken);
if (From.Aspects != null)
{
Result preResult = await From.Aspects.ExecutePre(HandlerInstance, HandlerContainer);
if (!preResult.Positive)
return preResult;
}
Result execResult = await HandlerInstance.Execute(HandlerContainer, cancellationToken);
if (!execResult.Positive)
return execResult;
if (From.Aspects != null)
{
Result postResult = await From.Aspects.ExecutePost(HandlerInstance, HandlerContainer);
if (!postResult.Positive)
return postResult;
}
return Result.Ok();
}
catch (OperationCanceledException)
{
@@ -0,0 +1,63 @@
using Telegrator.Aspects;
using Telegrator.Handlers;
using Telegrator.Handlers.Components;
namespace Telegrator.MadiatorCore.Descriptors
{
public sealed class DescriptorAspectsSet
{
public bool SelfPre { get; private set; }
public bool SelfPost { get; private set; }
public Type? TypedPre { get; private set; }
public Type? TypedPost { get; private set; }
public DescriptorAspectsSet(bool selfPre, Type? typedPre, bool selfPost, Type? typedPost)
{
SelfPre = selfPre;
SelfPost = selfPost;
TypedPre = typedPre;
TypedPost = typedPost;
}
public async Task<Result> ExecutePre(UpdateHandlerBase handler, IHandlerContainer container)
{
if (SelfPre)
{
if (handler is not IPreProcessor preProcessor)
throw new InvalidOperationException();
return await preProcessor.BeforeExecution(container);
}
if (TypedPre != null)
{
IPreProcessor preProcessor = (IPreProcessor)Activator.CreateInstance(TypedPre);
return await preProcessor.BeforeExecution(container);
}
return Result.Ok();
}
public async Task<Result> ExecutePost(UpdateHandlerBase handler, IHandlerContainer container)
{
if (SelfPost)
{
if (handler is not IPostProcessor postProcessor)
throw new InvalidOperationException();
return await postProcessor.AfterExecution(container);
}
if (TypedPost != null)
{
IPostProcessor postProcessor = (IPostProcessor)Activator.CreateInstance(TypedPost);
return await postProcessor.AfterExecution(container);
}
return Result.Ok();
}
}
}
@@ -76,7 +76,13 @@ namespace Telegrator.MadiatorCore.Descriptors
/// <summary>
/// The set of filters associated with this handler.
/// </summary>
public DescriptorFiltersSet Filters
public DescriptorFiltersSet? Filters
{
get;
protected set;
}
public DescriptorAspectsSet? Aspects
{
get;
protected set;
@@ -154,6 +160,7 @@ namespace Telegrator.MadiatorCore.Descriptors
UpdateType = handlerAttribute.Type;
Indexer = handlerAttribute.GetIndexer();
Filters = new DescriptorFiltersSet(handlerAttribute, stateKeeperAttribute, filters);
Aspects = HandlerInspector.GetAspects(handlerType);
DisplayString = HandlerInspector.GetDisplayName(handlerType);
}
@@ -2,6 +2,7 @@
using System.Reflection;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegrator.Aspects;
using Telegrator.Attributes.Components;
using Telegrator.Filters.Components;
@@ -80,5 +81,29 @@ namespace Telegrator.MadiatorCore.Descriptors
}
}
}
public static DescriptorAspectsSet GetAspects(Type handlerType)
{
bool selfPre = handlerType.GetInterface(nameof(IPreProcessor)) != null;
bool selfPost = handlerType.GetInterface(nameof(IPostProcessor)) != null;
Type? typedPre = null;
Type? typedPost = null;
if (!selfPre)
{
Attribute? preAttr = handlerType.GetCustomAttribute(typeof(BeforeExecutionAttribute<>));
if (preAttr != null)
typedPre = preAttr.GetType().GetGenericArguments()[0];
}
if (!selfPost)
{
Attribute? postAttr = handlerType.GetCustomAttribute(typeof(AfterExecutionAttribute<>));
if (postAttr != null)
typedPre = postAttr.GetType().GetGenericArguments()[0];
}
return new DescriptorAspectsSet(selfPre, typedPre, selfPost, typedPost);
}
}
}
+2 -2
View File
@@ -223,11 +223,11 @@ namespace Telegrator.Polling
};
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(_botInfo, update, update, data, []);
if (!descriptor.Filters.Validate(filterContext))
if (descriptor.Filters != null && !descriptor.Filters.Validate(filterContext))
return null;
UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken);
return new DescribedHandlerInfo(updateRouter, client, handlerInstance, filterContext, descriptor.DisplayString);
return new DescribedHandlerInfo(descriptor, updateRouter, client, handlerInstance, filterContext, descriptor.DisplayString);
}
/// <summary>
+1 -1
View File
@@ -68,7 +68,7 @@ namespace Telegrator.Providers
public virtual IHandlersCollection AddDescriptor(HandlerDescriptor descriptor)
{
if (MustHaveParameterlessCtor && !descriptor.HandlerType.HasParameterlessCtor())
throw new Exception();
throw new Exception("This handler (" + descriptor.HandlerType.FullName + "), must contain constructor without parameters.");
_allowedTypes.Union(descriptor.UpdateType);
MightAwaitAttribute[] mightAwaits = descriptor.HandlerType.GetCustomAttributes<MightAwaitAttribute>().ToArray();
+2
View File
@@ -886,6 +886,7 @@ namespace Telegrator
public static bool HasParameterlessCtor(this Type type)
=> type.GetConstructors().Any(ctor => ctor.GetParameters().Length == 0);
/*
/// <summary>
/// Invokes a "<paramref name="methodName"/>" method of an object
/// </summary>
@@ -948,6 +949,7 @@ namespace Telegrator
/// <returns></returns>
public static T? InvokeGenericMethod<T>(this object obj, string methodName, Type[] genericParameters, params object[]? parameters)
=> (T?)obj.GetType().GetMethod(methodName).InvokeGenericMethod(obj, genericParameters, parameters);
*/
/// <summary>
/// Checks is <paramref name="obj"/> has public properties