using System; using System.Linq; using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Svrnty.CQRS.Configuration; namespace Svrnty.CQRS.Grpc; /// /// Extension methods for CqrsBuilder to add gRPC support /// public static class CqrsBuilderExtensions { /// /// Adds gRPC support to the CQRS pipeline /// /// The CQRS builder /// Optional configuration for gRPC endpoints /// The CQRS builder for method chaining public static CqrsBuilder AddGrpc(this CqrsBuilder builder, Action? configure = null) { var options = new GrpcCqrsOptions(); configure?.Invoke(options); builder.Configuration.SetConfiguration(options); // Try to find and call the generated AddGrpcFromConfiguration method for service registration var addGrpcMethod = FindExtensionMethod("AddGrpcFromConfiguration", typeof(IServiceCollection)); if (addGrpcMethod != null) { addGrpcMethod.Invoke(null, new object[] { builder.Services }); } else { Console.WriteLine("Warning: AddGrpcFromConfiguration not found. gRPC services were not registered."); Console.WriteLine("Make sure your project has source generators enabled and references Svrnty.CQRS.Grpc.Generators."); DiagnoseGeneratedCode(); } // Register mapping callback for automatic endpoint mapping builder.Configuration.AddMappingCallback(app => { // Find the generated MapGrpcFromConfiguration method var mapGrpcMethod = FindExtensionMethod("MapGrpcFromConfiguration", typeof(Microsoft.AspNetCore.Routing.IEndpointRouteBuilder)); if (mapGrpcMethod != null) { mapGrpcMethod.Invoke(null, new object[] { app }); } }); return builder; } private static void DiagnoseGeneratedCode() { var entryAsm = Assembly.GetEntryAssembly(); if (entryAsm == null) { Console.WriteLine("Diagnostic: Entry assembly is null"); return; } Console.WriteLine($"Diagnostic: Entry assembly = {entryAsm.GetName().Name}"); try { var allTypes = entryAsm.GetTypes(); Console.WriteLine($"Diagnostic: Total types in entry assembly = {allTypes.Length}"); var grpcTypes = allTypes .Where(t => t.FullName?.Contains("Grpc") == true) .ToList(); if (grpcTypes.Any()) { Console.WriteLine("Diagnostic: Found Grpc-related types:"); foreach (var t in grpcTypes) { Console.WriteLine($" - {t.FullName} (IsClass={t.IsClass}, IsSealed={t.IsSealed}, IsPublic={t.IsPublic})"); // Check for our target method var method = t.GetMethod("AddGrpcFromConfiguration", BindingFlags.Static | BindingFlags.Public); if (method != null) Console.WriteLine($" -> HAS AddGrpcFromConfiguration method!"); } } else { Console.WriteLine("Diagnostic: No Grpc-related types found. Source generator did NOT run."); } } catch (ReflectionTypeLoadException ex) { Console.WriteLine($"Diagnostic: ReflectionTypeLoadException - {ex.Message}"); foreach (var le in ex.LoaderExceptions) { if (le != null) Console.WriteLine($" LoaderException: {le.Message}"); } } catch (Exception ex) { Console.WriteLine($"Diagnostic: Exception - {ex.GetType().Name}: {ex.Message}"); } } private static MethodInfo? FindExtensionMethod(string methodName, Type parameterType) { // Search through all loaded assemblies for the extension method foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { try { var types = assembly.GetTypes() .Where(t => t.IsClass && t.IsSealed && !t.IsGenericType && t.IsPublic); foreach (var type in types) { var method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public, null, new[] { parameterType }, null); if (method != null) return method; } } catch { // Skip assemblies that can't be inspected } } return null; } }