Skip to content

Commit

Permalink
Support custom grpc method
Browse files Browse the repository at this point in the history
  • Loading branch information
Kation committed Aug 13, 2024
1 parent 3cd9d87 commit baa4e64
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 29 deletions.
7 changes: 7 additions & 0 deletions src/Wodsoft.ComBoost.Grpc.AspNetCore/ComBoostGrpcBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public ComBoostGrpcBuilder(IServiceCollection services, IComBoostAspNetCoreBuild
{
Services = services ?? throw new ArgumentNullException(nameof(services));
AspNetCoreBuilder = aspNetCoreBuilder;
services.AddSingleton<IDomainGrpcMethodBuilder, DomainGrpcMethodProtobufBuilder>();
}

public IServiceCollection Services { get; }
Expand All @@ -23,5 +24,11 @@ public IComBoostGrpcBuilder AddTemplate<T>() where T : IDomainTemplate
Services.PostConfigure<DomainGrpcTemplateOptions>(options => options.AddTemplate<T>());
return this;
}

public IComBoostGrpcBuilder UseMethodBuilder<T>() where T : class, IDomainGrpcMethodBuilder
{
Services.AddSingleton<IDomainGrpcMethodBuilder, T>();
return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ namespace Wodsoft.ComBoost.Grpc.AspNetCore
{
public class DomainGrpcServiceMethodProvider : IServiceMethodProvider<DomainGrpcDiscoveryService>
{
private DomainGrpcTemplateOptions _options;
private readonly DomainGrpcTemplateOptions _options;
private readonly IDomainGrpcMethodBuilder _methodBuilder;

public DomainGrpcServiceMethodProvider(IOptions<DomainGrpcTemplateOptions> options)
public DomainGrpcServiceMethodProvider(IDomainGrpcMethodBuilder methodBuilder, IOptions<DomainGrpcTemplateOptions> options)
{
_methodBuilder = methodBuilder;
_options = options.Value;
}

Expand All @@ -25,12 +27,13 @@ public DomainGrpcServiceMethodProvider(IOptions<DomainGrpcTemplateOptions> optio
private static ConstructorInfo _GrpcContextConstructorInfo = typeof(DomainGrpcContext).GetConstructors()[0];
private static MethodInfo _GetServiceMethodInfo = typeof(ServiceProviderServiceExtensions).GetMethod("GetRequiredService", 1, BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(IServiceProvider) }, null)!;


public void OnServiceMethodDiscovery(ServiceMethodProviderContext<DomainGrpcDiscoveryService> context)
{
var contextType = context.GetType();
foreach (var type in _options.Types)
{
var builderType = typeof(DomainGrpcService<>).MakeGenericType(type);
builderType.GetMethod("Build", BindingFlags.NonPublic | BindingFlags.Static)!.Invoke(null, new object[] { _methodBuilder });
var serviceType = (Type)typeof(DomainGrpcService<>).MakeGenericType(type).GetField("ServiceType", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null)!;
foreach (var method in serviceType.GetTypeInfo().DeclaredMethods)
{
Expand Down
12 changes: 9 additions & 3 deletions src/Wodsoft.ComBoost.Grpc.AspNetCore/DomainGrpcService`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,15 @@ protected DomainGrpcResponse HandleResponse(Task task)
return response;
}

internal static readonly Type ServiceType;
internal static Type? ServiceType;
private static IDomainGrpcMethodBuilder? _MethodBuilder;

static DomainGrpcService()
public static IDomainGrpcMethodBuilder MethodBuilder => _MethodBuilder ?? throw new InvalidOperationException("Service not build yet.");

internal static void Build(IDomainGrpcMethodBuilder grpcMethodBuilder)
{
_MethodBuilder = grpcMethodBuilder;

var templateType = typeof(T);
TypeBuilder typeBuilder = DomainGrpcService.CreateType(templateType.Name.Trim('I'), typeof(DomainGrpcService<T>));

Expand Down Expand Up @@ -225,9 +230,10 @@ static DomainGrpcService()
}
//Create static Method<,> field and set value
var methodField = typeBuilder.DefineField("_Method_" + method.Name + "_" + methodIndex, typeof(Method<,>).MakeGenericType(requestType, responseType), FieldAttributes.Private | FieldAttributes.Static);
staticILGenerator.Emit(OpCodes.Call, typeof(DomainGrpcService<T>).GetProperty("MethodBuilder", BindingFlags.Public | BindingFlags.Static)!.GetMethod!);
staticILGenerator.Emit(OpCodes.Ldstr, methodServiceName ?? serviceName ?? DomainService.GetServiceName(typeof(T)));
staticILGenerator.Emit(OpCodes.Ldstr, methodName ?? (method.Name + "_" + methodIndex));
staticILGenerator.Emit(OpCodes.Call, typeof(DomainGrpcMethod<,>).MakeGenericType(requestType, responseType).GetMethod("CreateMethod")!);
staticILGenerator.Emit(OpCodes.Callvirt, typeof(IDomainGrpcMethodBuilder).GetMethod("CreateMethod")!.MakeGenericMethod(requestType, responseType));
staticILGenerator.Emit(OpCodes.Stsfld, methodField);
}

Expand Down
3 changes: 3 additions & 0 deletions src/Wodsoft.ComBoost.Grpc.AspNetCore/IComBoostGrpcBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ public interface IComBoostGrpcBuilder
IComBoostGrpcBuilder AddTemplate<T>() where T : IDomainTemplate;

IComBoostAspNetCoreBuilder AspNetCoreBuilder { get; }

IComBoostGrpcBuilder UseMethodBuilder<T>()
where T : class, IDomainGrpcMethodBuilder;
}
}
10 changes: 9 additions & 1 deletion src/Wodsoft.ComBoost.Grpc.Client/ComBoostGrpcBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ namespace Wodsoft.ComBoost.Grpc.Client
public class ComBoostGrpcBuilder : IComBoostGrpcBuilder
{
private IDomainGrpcCallOptionsHandler? _callOptionsHandler;
private IDomainGrpcMethodBuilder _methodBuilder;

public ComBoostGrpcBuilder(IServiceCollection services, IComBoostBuilder comBoostBuilder)
{
Services = services ?? throw new ArgumentNullException(nameof(services));
ComBoostBuilder = comBoostBuilder;
_methodBuilder = new DomainGrpcMethodProtobufBuilder();
}

public IServiceCollection Services { get; }
Expand All @@ -22,7 +24,7 @@ public ComBoostGrpcBuilder(IServiceCollection services, IComBoostBuilder comBoos

public IComBoostGrpcServiceBuilder AddService(Uri address, Func<IServiceProvider, GrpcChannelOptions> optionsFactory)
{
ComBoostGrpcServiceBuilder builder = new ComBoostGrpcServiceBuilder(Services, address ?? throw new ArgumentNullException(nameof(address)), optionsFactory, _callOptionsHandler);
ComBoostGrpcServiceBuilder builder = new ComBoostGrpcServiceBuilder(Services, address ?? throw new ArgumentNullException(nameof(address)), optionsFactory, _callOptionsHandler, _methodBuilder);
return builder;
}

Expand All @@ -31,5 +33,11 @@ public IComBoostGrpcBuilder UseCallOptionsHandler(IDomainGrpcCallOptionsHandler
_callOptionsHandler = handler;
return this;
}

public IComBoostGrpcBuilder UseMethodBuilder(IDomainGrpcMethodBuilder methodBuilder)
{
_methodBuilder = methodBuilder ?? throw new ArgumentNullException(nameof(methodBuilder));
return this;
}
}
}
16 changes: 13 additions & 3 deletions src/Wodsoft.ComBoost.Grpc.Client/ComBoostGrpcServiceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ public class ComBoostGrpcServiceBuilder : IComBoostGrpcServiceBuilder
private Uri _address;
private Func<IServiceProvider, GrpcChannelOptions> _optionsFactory;
private IDomainGrpcCallOptionsHandler? _callOptionsHandler;
private IDomainGrpcMethodBuilder _methodBuilder;

public ComBoostGrpcServiceBuilder(IServiceCollection services, Uri address, Func<IServiceProvider, GrpcChannelOptions> optionsFactory, IDomainGrpcCallOptionsHandler? callOptionsHandler)
public ComBoostGrpcServiceBuilder(IServiceCollection services, Uri address, Func<IServiceProvider, GrpcChannelOptions> optionsFactory, IDomainGrpcCallOptionsHandler? callOptionsHandler, IDomainGrpcMethodBuilder methodBuilder)
{
Services = services ?? throw new ArgumentNullException(nameof(services));
_address = address ?? throw new ArgumentNullException(nameof(address));
_optionsFactory = optionsFactory ?? throw new ArgumentNullException(nameof(optionsFactory));
_callOptionsHandler = callOptionsHandler;
_methodBuilder = methodBuilder;
}

public IServiceCollection Services { get; }
Expand All @@ -33,14 +35,22 @@ public IComBoostGrpcServiceBuilder UseCallOptionsHandler(IDomainGrpcCallOptionsH
return this;
}

public IComBoostGrpcServiceBuilder UseMethodBuilder(IDomainGrpcMethodBuilder methodBuilder)
{
_methodBuilder = methodBuilder ?? throw new ArgumentNullException(nameof(methodBuilder));
return this;
}

public IComBoostGrpcServiceBuilder UseTemplate<T>(CallOptions callOptions = default)
where T : class, IDomainTemplate
{
if (_callOptionsHandler != null)
_callOptionsHandler.Handle(typeof(T), ref callOptions);
Services.AddSingleton<IDomainTemplateDescriptor<T>, GrpcTemplateBuilder<T>>(sp =>
new GrpcTemplateBuilder<T>(GrpcChannel.ForAddress(_address, _optionsFactory(sp)), callOptions)
);
{
GrpcTemplateBuilder<T>.Build(_methodBuilder);
return new GrpcTemplateBuilder<T>(GrpcChannel.ForAddress(_address, _optionsFactory(sp)), callOptions);
});
Services.AddTransient<T>(sp =>
{
var contextProvider = sp.GetRequiredService<IDomainContextProvider>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Reflection;
using System.Text;
using Wodsoft.ComBoost;
using Wodsoft.ComBoost.Grpc;
using Wodsoft.ComBoost.Grpc.Client;

namespace Microsoft.Extensions.DependencyInjection
Expand Down Expand Up @@ -36,6 +37,12 @@ public static IComBoostGrpcBuilder UseCallOptionsHandler<T>(this IComBoostGrpcBu
return builder.UseCallOptionsHandler(new T());
}

public static IComBoostGrpcBuilder UseMethodBuilder<T>(this IComBoostGrpcBuilder builder)
where T : IDomainGrpcMethodBuilder, new()
{
return builder.UseMethodBuilder(new T());
}

public static IComBoostGrpcServiceBuilder AddService(this IComBoostGrpcBuilder builder, Uri address)
{
return builder.AddService(address, sp => new GrpcChannelOptions());
Expand All @@ -52,6 +59,12 @@ public static IComBoostGrpcServiceBuilder UseCallOptionsHandler<T>(this IComBoos
return builder.UseCallOptionsHandler(new T());
}

public static IComBoostGrpcServiceBuilder UseMethodBuilder<T>(this IComBoostGrpcServiceBuilder builder)
where T : IDomainGrpcMethodBuilder, new()
{
return builder.UseMethodBuilder(new T());
}

private readonly static MethodInfo _UseTemplateMethod = typeof(IComBoostGrpcServiceBuilder).GetMethod("UseTemplate");
public static IComBoostGrpcServiceBuilder UseTemplateInAssembly(this IComBoostGrpcServiceBuilder builder, string serviceName, Assembly assembly, CallOptions callOptions = default)
{
Expand Down
13 changes: 9 additions & 4 deletions src/Wodsoft.ComBoost.Grpc.Client/GrpcTemplateBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,14 @@ private async Task HandleResponseAsync(DomainGrpcResponse response)
public class GrpcTemplateBuilder<T> : GrpcTemplateBuilder, IDomainTemplateDescriptor<T>
where T : class, IDomainTemplate
{
private static Type _TemplateType;
private static Type? _TemplateType;
private static IDomainGrpcMethodBuilder? _MethodBuilder;

static GrpcTemplateBuilder()
public static IDomainGrpcMethodBuilder MethodBuilder => _MethodBuilder ?? throw new InvalidOperationException("Template not build yet.");

internal static void Build(IDomainGrpcMethodBuilder grpcMethodBuilder)
{
_MethodBuilder = grpcMethodBuilder;
var type = typeof(T);
if (!type.IsInterface)
throw new NotSupportedException("Not support non interface type as a template.");
Expand Down Expand Up @@ -214,9 +218,10 @@ static GrpcTemplateBuilder()
}
//Create static Method<,> field and set value
var methodField = typeBuilder.DefineField("_Method_" + method.Name + "_" + methodIndex, typeof(Method<,>).MakeGenericType(requestType, responseType), FieldAttributes.Private | FieldAttributes.Static);
staticILGenerator.Emit(OpCodes.Call, typeof(GrpcTemplateBuilder<T>).GetProperty("MethodBuilder", BindingFlags.Public | BindingFlags.Static).GetMethod);
staticILGenerator.Emit(OpCodes.Ldstr, methodServiceName ?? serviceName ?? DomainService.GetServiceName(type));
staticILGenerator.Emit(OpCodes.Ldstr, methodName ?? (method.Name + "_" + methodIndex));
staticILGenerator.Emit(OpCodes.Call, typeof(DomainGrpcMethod<,>).MakeGenericType(requestType, responseType).GetMethod("CreateMethod"));
staticILGenerator.Emit(OpCodes.Callvirt, typeof(IDomainGrpcMethodBuilder).GetMethod("CreateMethod").MakeGenericMethod(requestType, responseType));
staticILGenerator.Emit(OpCodes.Stsfld, methodField);

//Create method for class
Expand Down Expand Up @@ -279,7 +284,7 @@ public GrpcTemplateBuilder(GrpcChannel channel, CallOptions callOptions)

public T GetTemplate(IDomainContext context)
{
var template = (T)Activator.CreateInstance(_TemplateType, _channel, _callOptions);
var template = (T)Activator.CreateInstance(_TemplateType!, _channel, _callOptions);
template.Context = context;
return template;
}
Expand Down
2 changes: 2 additions & 0 deletions src/Wodsoft.ComBoost.Grpc.Client/IComBoostGrpcBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public interface IComBoostGrpcBuilder
IComBoostBuilder ComBoostBuilder { get; }

IComBoostGrpcBuilder UseCallOptionsHandler(IDomainGrpcCallOptionsHandler handler);

IComBoostGrpcBuilder UseMethodBuilder(IDomainGrpcMethodBuilder methodBuilder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public interface IComBoostGrpcServiceBuilder

IComBoostGrpcServiceBuilder UseCallOptionsHandler(IDomainGrpcCallOptionsHandler handler);

IComBoostGrpcServiceBuilder UseMethodBuilder(IDomainGrpcMethodBuilder methodBuilder);

IComBoostGrpcServiceBuilder UseTemplate<T>(CallOptions callOptions = default(CallOptions)) where T : class, IDomainTemplate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,17 @@

namespace Wodsoft.ComBoost.Grpc
{
public class DomainGrpcMethod<TRequest, TResponse>
where TRequest : class, IMessage, new()
where TResponse : class, IMessage, new()
public class DomainGrpcMethodProtobufBuilder : IDomainGrpcMethodBuilder
{
public static Method<TRequest, TResponse> CreateMethod(string serviceName, string methodName)
public Method<TRequest, TResponse> CreateMethod<TRequest, TResponse>(string serviceName, string methodName)
{
return new Method<TRequest, TResponse>(MethodType.Unary, serviceName, methodName, new Marshaller<TRequest>((request) =>
{
try
{
MemoryStream stream = new MemoryStream();
var output = new CodedOutputStream(stream, true);
output.WriteRawMessage(request);
output.Flush();
Message.Serialize(stream, request);
return stream.ToArray();
}
catch (Exception ex)
Expand All @@ -34,8 +31,7 @@ public static Method<TRequest, TResponse> CreateMethod(string serviceName, strin
try
{
var input = new CodedInputStream(data);
var value = new TRequest();
input.ReadRawMessage(value);
var value = Message.Deserialize<TRequest>(input);
return value;
}
catch (Exception ex)
Expand All @@ -47,9 +43,7 @@ public static Method<TRequest, TResponse> CreateMethod(string serviceName, strin
try
{
MemoryStream stream = new MemoryStream();
var output = new CodedOutputStream(stream, true);
output.WriteRawMessage(response);
output.Flush();
Message.Serialize(stream, response);
return stream.ToArray();
}
catch (Exception ex)
Expand All @@ -61,8 +55,7 @@ public static Method<TRequest, TResponse> CreateMethod(string serviceName, strin
try
{
var input = new CodedInputStream(data);
var value = new TResponse();
input.ReadRawMessage(value);
var value = Message.Deserialize<TResponse>(input);
return value;
}
catch (Exception ex)
Expand Down
13 changes: 13 additions & 0 deletions src/Wodsoft.ComBoost.Grpc/IDomainGrpcMethodBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Google.Protobuf;
using Grpc.Core;
using System;
using System.Collections.Generic;
using System.Text;

namespace Wodsoft.ComBoost.Grpc
{
public interface IDomainGrpcMethodBuilder
{
Method<TRequest, TResponse> CreateMethod<TRequest, TResponse>(string serviceName, string methodName);
}
}
Loading

0 comments on commit baa4e64

Please sign in to comment.