Created
February 9, 2024 08:25
-
-
Save dbones/15f3f809d7c4acfd7fc095cd8b648e8f to your computer and use it in GitHub Desktop.
Mimicking a minimal Api
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel.DataAnnotations; | |
using System.Linq.Expressions; | |
using Microsoft.Extensions.DependencyInjection; | |
public delegate Task MinimalCommand<T>(IServiceProvider scope, T instnace); | |
public class AppBuilder | |
{ | |
readonly IServiceProvider _serviceProvider; | |
readonly Dictionary<Type, object> _commands = new(); | |
public AppBuilder(IServiceProvider serviceProvider) | |
{ | |
_serviceProvider = serviceProvider; | |
} | |
public void Map<T>(Delegate impl2) | |
{ | |
var handler = CompileHandler<T>(impl2); | |
_commands.Add(typeof(T), handler); | |
} | |
static MinimalCommand<T> CompileHandler<T>(Delegate impl) | |
{ | |
var scopeParameter = Expression.Parameter(typeof(IServiceProvider), "scope"); | |
var instanceParameter = Expression.Parameter(typeof(T), "instance"); | |
var methodInfo = impl.Method; | |
var targetExpression = impl.Target != null ? | |
Expression.Constant(impl.Target) : null; | |
var parameters = new List<Expression>(); | |
foreach (var parameter in methodInfo.GetParameters()) | |
{ | |
if (parameter.ParameterType == typeof(T)) | |
{ | |
parameters.Add(instanceParameter); | |
} | |
else | |
{ | |
var resolveParamExpr = Expression.Call( | |
typeof(ServiceProviderServiceExtensions), nameof(ServiceProviderServiceExtensions.GetRequiredService), | |
null, scopeParameter, Expression.Constant(parameter.ParameterType)); | |
var resolveParam = Expression.Convert(resolveParamExpr, parameter.ParameterType); | |
parameters.Add(resolveParam); | |
} | |
} | |
Expression methodCall; | |
if (impl.Target != null) | |
{ | |
methodCall = Expression.Call( | |
Expression.Constant(impl.Target), | |
methodInfo, | |
parameters); | |
} | |
else | |
{ | |
methodCall = Expression.Call( | |
methodInfo, | |
parameters); | |
} | |
var methodTaskResultType = methodInfo.ReturnType; | |
if (methodTaskResultType != typeof(Task)) | |
{ | |
methodCall = Expression.Call( | |
typeof(Task), | |
nameof(Task.FromResult), | |
new[] { methodTaskResultType }, | |
methodCall); | |
} | |
var body = Expression.Block(methodCall); | |
var lambda = Expression.Lambda<MinimalCommand<T>>(body, scopeParameter, instanceParameter); | |
return lambda.Compile(); | |
} | |
public async Task Execute<T>(T payload) | |
{ | |
if (!_commands.TryGetValue(typeof(T), out var commandBoxed)) | |
{ | |
throw new NotSupportedException($"no minimal Api for {typeof(T).FullName}"); | |
} | |
var command = (MinimalCommand<T>)commandBoxed; | |
await command(_serviceProvider, payload); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//this is how we can inject all on all the params of a delegate.... | |
var app = new AppBuilder(serviceProvider); | |
app.Map<Hello>(async (Hello message, ILogger<Program> logger) => | |
{ | |
logger.LogInformation("hi {Name}", message.Name); | |
return Task.CompletedTask; | |
}); | |
app.Execute(new Hello() { Name = "Dave" }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment