checkpoint
This commit is contained in:
parent
facc8d7851
commit
e19fad2baa
@ -24,8 +24,8 @@ public static class CqrsBuilderExtensions
|
|||||||
configure?.Invoke(options);
|
configure?.Invoke(options);
|
||||||
builder.Configuration.SetConfiguration(options);
|
builder.Configuration.SetConfiguration(options);
|
||||||
|
|
||||||
// Try to find and call the generated AddGrpcFromConfiguration method
|
// Try to find and call the generated AddGrpcFromConfiguration method for service registration
|
||||||
var addGrpcMethod = FindExtensionMethod("AddGrpcFromConfiguration");
|
var addGrpcMethod = FindExtensionMethod("AddGrpcFromConfiguration", typeof(IServiceCollection));
|
||||||
if (addGrpcMethod != null)
|
if (addGrpcMethod != null)
|
||||||
{
|
{
|
||||||
addGrpcMethod.Invoke(null, new object[] { builder.Services });
|
addGrpcMethod.Invoke(null, new object[] { builder.Services });
|
||||||
@ -36,10 +36,21 @@ public static class CqrsBuilderExtensions
|
|||||||
Console.WriteLine("Make sure your project has source generators enabled and references Svrnty.CQRS.Grpc.Generators.");
|
Console.WriteLine("Make sure your project has source generators enabled and references Svrnty.CQRS.Grpc.Generators.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodInfo? FindExtensionMethod(string methodName)
|
private static MethodInfo? FindExtensionMethod(string methodName, Type parameterType)
|
||||||
{
|
{
|
||||||
// Search through all loaded assemblies for the extension method
|
// Search through all loaded assemblies for the extension method
|
||||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
@ -54,7 +65,7 @@ public static class CqrsBuilderExtensions
|
|||||||
var method = type.GetMethod(methodName,
|
var method = type.GetMethod(methodName,
|
||||||
BindingFlags.Static | BindingFlags.Public,
|
BindingFlags.Static | BindingFlags.Public,
|
||||||
null,
|
null,
|
||||||
new[] { typeof(IServiceCollection) },
|
new[] { parameterType },
|
||||||
null);
|
null);
|
||||||
|
|
||||||
if (method != null)
|
if (method != null)
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Svrnty.CQRS.Configuration;
|
using Svrnty.CQRS.Configuration;
|
||||||
|
|
||||||
namespace Svrnty.CQRS.MinimalApi;
|
namespace Svrnty.CQRS.MinimalApi;
|
||||||
@ -20,6 +23,71 @@ public static class CqrsBuilderExtensions
|
|||||||
var options = new MinimalApiCqrsOptions();
|
var options = new MinimalApiCqrsOptions();
|
||||||
configure?.Invoke(options);
|
configure?.Invoke(options);
|
||||||
builder.Configuration.SetConfiguration(options);
|
builder.Configuration.SetConfiguration(options);
|
||||||
|
|
||||||
|
// Register mapping callback for automatic endpoint mapping
|
||||||
|
builder.Configuration.AddMappingCallback(app =>
|
||||||
|
{
|
||||||
|
if (app is not WebApplication webApp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var config = webApp.Services.GetService(typeof(CqrsConfiguration)) as CqrsConfiguration;
|
||||||
|
var minimalApiOptions = config?.GetConfiguration<MinimalApiCqrsOptions>();
|
||||||
|
|
||||||
|
if (minimalApiOptions == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Map commands if enabled
|
||||||
|
if (minimalApiOptions.MapCommands)
|
||||||
|
{
|
||||||
|
webApp.MapSvrntyCommands(minimalApiOptions.CommandRoutePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map queries if enabled
|
||||||
|
if (minimalApiOptions.MapQueries)
|
||||||
|
{
|
||||||
|
webApp.MapSvrntyQueries(minimalApiOptions.QueryRoutePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map dynamic queries if enabled (automatically included)
|
||||||
|
if (minimalApiOptions.MapDynamicQueries)
|
||||||
|
{
|
||||||
|
// Try to find the DynamicQuery.MinimalApi assembly and call MapSvrntyDynamicQueries
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Force load the assembly by looking for a known type from it
|
||||||
|
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||||
|
var dynamicQueryAssembly = assemblies.FirstOrDefault(a => a.GetName().Name == "Svrnty.CQRS.DynamicQuery.MinimalApi");
|
||||||
|
|
||||||
|
if (dynamicQueryAssembly == null)
|
||||||
|
{
|
||||||
|
// Try to load it by finding the type
|
||||||
|
var extensionType = Type.GetType("Svrnty.CQRS.DynamicQuery.MinimalApi.EndpointRouteBuilderExtensions, Svrnty.CQRS.DynamicQuery.MinimalApi");
|
||||||
|
if (extensionType != null)
|
||||||
|
{
|
||||||
|
dynamicQueryAssembly = extensionType.Assembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynamicQueryAssembly != null)
|
||||||
|
{
|
||||||
|
var extensionType = dynamicQueryAssembly.GetType("Svrnty.CQRS.DynamicQuery.MinimalApi.EndpointRouteBuilderExtensions");
|
||||||
|
if (extensionType != null)
|
||||||
|
{
|
||||||
|
var mapMethod = extensionType.GetMethod("MapSvrntyDynamicQueries", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
if (mapMethod != null)
|
||||||
|
{
|
||||||
|
mapMethod.Invoke(null, new object[] { webApp, minimalApiOptions.DynamicQueryRoutePrefix });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Warning: Could not map dynamic queries: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,101 +1,21 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Routing;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Svrnty.CQRS.Configuration;
|
using Svrnty.CQRS.Configuration;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Svrnty.CQRS.MinimalApi;
|
namespace Svrnty.CQRS.MinimalApi;
|
||||||
|
|
||||||
public static class WebApplicationExtensions
|
public static class WebApplicationExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps Svrnty CQRS endpoints based on configuration (supports both gRPC and MinimalApi)
|
/// Maps all configured CQRS endpoints (gRPC, MinimalApi, and Dynamic Queries).
|
||||||
|
/// This method is framework-agnostic and executes mapping callbacks registered by extension packages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static WebApplication UseSvrntyCqrs(this WebApplication app)
|
public static WebApplication UseSvrntyCqrs(this WebApplication app)
|
||||||
{
|
{
|
||||||
var config = app.Services.GetService<CqrsConfiguration>();
|
var config = app.Services.GetRequiredService<CqrsConfiguration>();
|
||||||
|
config.ExecuteMappingCallbacks(app);
|
||||||
// Handle gRPC configuration if available
|
|
||||||
// Note: GrpcCqrsOptions type is from Svrnty.CQRS.Grpc package
|
|
||||||
var grpcOptionsType = Type.GetType("Svrnty.CQRS.Grpc.GrpcCqrsOptions, Svrnty.CQRS.Grpc");
|
|
||||||
if (grpcOptionsType != null && config != null)
|
|
||||||
{
|
|
||||||
var getConfigMethod = typeof(CqrsConfiguration).GetMethod("GetConfiguration")!.MakeGenericMethod(grpcOptionsType);
|
|
||||||
var grpcOptions = getConfigMethod.Invoke(config, null);
|
|
||||||
|
|
||||||
if (grpcOptions != null)
|
|
||||||
{
|
|
||||||
// Try to find and call MapGrpcFromConfiguration extension method via reflection
|
|
||||||
// This is generated by the source generator in consumer projects
|
|
||||||
var grpcMethod = FindExtensionMethod("MapGrpcFromConfiguration");
|
|
||||||
if (grpcMethod != null)
|
|
||||||
{
|
|
||||||
grpcMethod.Invoke(null, new object[] { app });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("Warning: MapGrpcFromConfiguration not found. gRPC endpoints were not mapped.");
|
|
||||||
Console.WriteLine("Make sure your project references Svrnty.CQRS.Grpc and has source generators enabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle MinimalApi configuration if available
|
|
||||||
var minimalApiOptions = config?.GetConfiguration<MinimalApiCqrsOptions>();
|
|
||||||
if (minimalApiOptions != null)
|
|
||||||
{
|
|
||||||
if (minimalApiOptions.MapCommands)
|
|
||||||
{
|
|
||||||
app.MapSvrntyCommands(minimalApiOptions.CommandRoutePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minimalApiOptions.MapQueries)
|
|
||||||
{
|
|
||||||
app.MapSvrntyQueries(minimalApiOptions.QueryRoutePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add dynamic query mapping when available
|
|
||||||
// if (minimalApiOptions.MapDynamicQueries)
|
|
||||||
// {
|
|
||||||
// app.MapSvrntyDynamicQueries(minimalApiOptions.DynamicQueryRoutePrefix);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodInfo? FindExtensionMethod(string methodName)
|
|
||||||
{
|
|
||||||
// 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[] { typeof(IEndpointRouteBuilder) },
|
|
||||||
null);
|
|
||||||
|
|
||||||
if (method != null)
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Skip assemblies that can't be inspected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ namespace Svrnty.CQRS.Configuration;
|
|||||||
public class CqrsConfiguration
|
public class CqrsConfiguration
|
||||||
{
|
{
|
||||||
private readonly Dictionary<Type, object> _configurations = new();
|
private readonly Dictionary<Type, object> _configurations = new();
|
||||||
|
private readonly List<Action<object>> _mappingCallbacks = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets a configuration object for a specific type
|
/// Sets a configuration object for a specific type
|
||||||
@ -35,4 +36,27 @@ public class CqrsConfiguration
|
|||||||
{
|
{
|
||||||
return _configurations.ContainsKey(typeof(T));
|
return _configurations.ContainsKey(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a callback to be executed during endpoint mapping.
|
||||||
|
/// Used by extension packages to register their endpoint mapping logic.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">Callback that accepts an application builder (typically WebApplication)</param>
|
||||||
|
public void AddMappingCallback(Action<object> callback)
|
||||||
|
{
|
||||||
|
_mappingCallbacks.Add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes all registered mapping callbacks.
|
||||||
|
/// Called by UseSvrntyCqrs() to map endpoints from all configured packages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app">The application builder (typically WebApplication)</param>
|
||||||
|
public void ExecuteMappingCallbacks(object app)
|
||||||
|
{
|
||||||
|
foreach (var callback in _mappingCallbacks)
|
||||||
|
{
|
||||||
|
callback(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ using Svrnty.CQRS.Grpc;
|
|||||||
using Svrnty.Sample;
|
using Svrnty.Sample;
|
||||||
using Svrnty.CQRS.MinimalApi;
|
using Svrnty.CQRS.MinimalApi;
|
||||||
using Svrnty.CQRS.DynamicQuery;
|
using Svrnty.CQRS.DynamicQuery;
|
||||||
using Svrnty.CQRS.DynamicQuery.MinimalApi;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@ -39,7 +38,9 @@ builder.Services.AddSvrntyCqrs(cqrs =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Enable MinimalApi endpoints
|
// Enable MinimalApi endpoints
|
||||||
cqrs.AddMinimalApi();
|
cqrs.AddMinimalApi(configure =>
|
||||||
|
{
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
@ -47,12 +48,9 @@ builder.Services.AddSwaggerGen();
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Map all configured CQRS endpoints (gRPC and MinimalApi)
|
// Map all configured CQRS endpoints (gRPC, MinimalApi, and Dynamic Queries)
|
||||||
app.UseSvrntyCqrs();
|
app.UseSvrntyCqrs();
|
||||||
|
|
||||||
// Map dynamic queries manually for now
|
|
||||||
app.MapSvrntyDynamicQueries();
|
|
||||||
|
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user