dotnet-cqrs/Svrnty.CQRS.Grpc/CqrsBuilderExtensions.cs
David Nguyen 20147bfec7
All checks were successful
Publish NuGets / build (release) Successful in 37s
Add diagnostic logging when gRPC generated code not found
When AddGrpcFromConfiguration method is not found via reflection,
logs detailed diagnostics to help identify the root cause:
- Entry assembly name and total type count
- All Grpc-related types with their IsClass/IsSealed/IsPublic flags
- Whether the target method exists on each type
- ReflectionTypeLoadException details if type loading fails

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:10:18 -05:00

137 lines
4.8 KiB
C#

using System;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Configuration;
namespace Svrnty.CQRS.Grpc;
/// <summary>
/// Extension methods for CqrsBuilder to add gRPC support
/// </summary>
public static class CqrsBuilderExtensions
{
/// <summary>
/// Adds gRPC support to the CQRS pipeline
/// </summary>
/// <param name="builder">The CQRS builder</param>
/// <param name="configure">Optional configuration for gRPC endpoints</param>
/// <returns>The CQRS builder for method chaining</returns>
public static CqrsBuilder AddGrpc(this CqrsBuilder builder, Action<GrpcCqrsOptions>? 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;
}
}