using System; using System.Linq.Expressions; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Svrnty.CQRS.Abstractions.Attributes; namespace Svrnty.CQRS.Abstractions.Discovery; public class QueryMeta : IQueryMeta { private readonly string _name; public QueryMeta(Type queryType, Type serviceType, Type queryResultType) { QueryType = queryType; ServiceType = serviceType; QueryResultType = queryResultType; // Cache reflection and computed value once in constructor var nameAttribute = queryType.GetCustomAttribute(); _name = nameAttribute?.Name ?? queryType.Name.Replace("Query", string.Empty); // Build compiled delegate for handler invocation CompiledInvoker = BuildCompiledInvoker(serviceType, queryType, queryResultType); } private static Func> BuildCompiledInvoker( Type serviceType, Type queryType, Type resultType) { // Parameters: (object handler, object query, CancellationToken ct) var handlerParam = Expression.Parameter(typeof(object), "handler"); var queryParam = Expression.Parameter(typeof(object), "query"); var ctParam = Expression.Parameter(typeof(CancellationToken), "ct"); // Cast handler to actual handler type var handlerCast = Expression.Convert(handlerParam, serviceType); // Cast query to actual query type var queryCast = Expression.Convert(queryParam, queryType); // Get HandleAsync method with correct return type (queries always return Task) var expectedReturnType = typeof(Task<>).MakeGenericType(resultType); var handleMethod = serviceType.GetMethod("HandleAsync", BindingFlags.Public | BindingFlags.Instance, null, new[] { queryType, typeof(CancellationToken) }, null); if (handleMethod == null || handleMethod.ReturnType != expectedReturnType) throw new InvalidOperationException($"HandleAsync method with return type {expectedReturnType} not found on {serviceType}"); // Call handler.HandleAsync(query, ct) - returns Task var methodCall = Expression.Call(handlerCast, handleMethod, queryCast, ctParam); // Use helper method to box the result var asyncHelperMethod = typeof(QueryMeta).GetMethod(nameof(InvokeAndBox), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)! .MakeGenericMethod(resultType); var invokeCall = Expression.Call(asyncHelperMethod, methodCall); // Compile to delegate var lambda = Expression.Lambda>>( invokeCall, handlerParam, queryParam, ctParam); return lambda.Compile(); } private static async Task InvokeAndBox(Task task) { var result = await task; return result; } public virtual string Name => _name; public virtual Type QueryType { get; } public virtual Type ServiceType { get; } public virtual Type QueryResultType { get; } public virtual string Category => "BasicQuery"; public string LowerCamelCaseName { get { // Use virtual Name property so derived classes can override var name = Name; if (string.IsNullOrEmpty(name)) return name; var firstLetter = char.ToLowerInvariant(name[0]); return $"{firstLetter}{name[1..]}"; } } /// /// Compiled delegate for invoking the handler without reflection. /// Signature: (object handler, object query, CancellationToken ct) => Task /// public Func> CompiledInvoker { get; } }