protected override void AddGenericBlockingMethodCore <TRequest, TReturn, TResponseReturn>( Func <TService, TRequest, CancellationToken, TReturn> serviceCaller, Func <TReturn, TResponseReturn>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, INetGrpcBinder <TService> binder) { var serializer = serviceStub.Serializer; Task <RpcResponse <TResponseReturn> > Handler(NetGrpcServiceActivator <TService> activator, TRequest request, ServerCallContext context) => serviceStub.CallBlockingMethod( request, new GrpcCallContext(context), serviceCaller, responseConverter, faultHandler, serializer, activator.ServiceProvider).AsTask(); var methodStub = GrpcMethodDefinition.Create <TRequest, RpcResponse <TResponseReturn> >( MethodType.Unary, operationInfo.FullServiceName, operationInfo.Name, serializer); binder.AddUnaryMethod(methodStub, operationInfo.Metadata, Handler); }
private Type CreateResponseType(ModuleBuilder dynamicModuleBuilder, RpcOperationInfo opInfo) { try { var typeBuilder = dynamicModuleBuilder.DefineType($"{opInfo.Service.Name}_{opInfo.Name}Reply"); var contractCtor = typeof(ProtoContractAttribute).GetConstructor(Type.EmptyTypes); var attribBuilder = new CustomAttributeBuilder(contractCtor, Array.Empty <object>()); typeBuilder.SetCustomAttribute(attribBuilder); var errorField = typeBuilder.DefineField("Error", typeof(RpcError), FieldAttributes.Public); AddMemberAttribute(errorField, 1); if (!Equals(opInfo.ResponseReturnType, typeof(void))) { var resultField = typeBuilder.DefineField("Result", opInfo.ResponseReturnType, FieldAttributes.Public); AddMemberAttribute(resultField, 2); } _ = opInfo.Method.GetParameters(); return(typeBuilder.CreateType()); } catch (Exception e) { LogWarning(2, $"Failed to create reply type for operation '{opInfo.FullName}'", e); } return(null); }
protected override void AddCallbackMethodCore <TRequest, TReturn, TResponseReturn>( Func <TService, TRequest, Action <TReturn>, CancellationToken, Task> serviceCaller, Func <TReturn, TResponseReturn>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, INetGrpcBinder <TService> binder) { var serializer = serviceStub.Serializer; ServerStreamingServerMethod <NetGrpcServiceActivator <TService>, TRequest, TResponseReturn> handler = (activator, request, responseStream, context) => { return(serviceStub.CallCallbackMethod( request, activator.ServiceProvider, new GrpcCallContext(context), new GrpcAsyncStreamWriter <TResponseReturn>(responseStream), serviceCaller, responseConverter, faultHandler, serializer).AsTask()); }; var methodStub = GrpcMethodDefinition.Create <TRequest, TResponseReturn>( MethodType.ServerStreaming, operationInfo.FullServiceName, operationInfo.Name, serializer); binder.AddServerStreamingMethod(methodStub, operationInfo.Metadata, handler); }
protected override void AddCallbackMethodCore <TRequest, TReturn, TResponseReturn>( Func <TService, TRequest, Action <TReturn>, CancellationToken, Task> serviceCaller, Func <TReturn, TResponseReturn>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, IGrpcMethodBinder binder) where TResponseReturn : class { var serializer = serviceStub.Serializer; GrpcCore.ServerStreamingServerMethod <TRequest, TResponseReturn> handler = (request, responseStream, context) => { using (var serviceScope = CreateServiceScope(serviceStub)) { return(serviceStub.CallCallbackMethod( request, serviceScope?.ServiceProvider, new GrpcCallContext(context), new GrpcAsyncStreamWriter <TResponseReturn>(responseStream), serviceCaller, responseConverter, faultHandler, serializer).AsTask()); } }; binder.AddMethod( GrpcMethodDefinition.Create <TRequest, TResponseReturn>( GrpcCore.MethodType.ServerStreaming, operationInfo.FullServiceName, operationInfo.Name, serviceStub.Serializer), handler); }
protected override void AddGenericBlockingMethodCore <TRequest, TReturn, TResponseReturn>( Func <TService, TRequest, CancellationToken, TReturn> serviceCaller, Func <TReturn, TResponseReturn>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, IGrpcMethodBinder binder) { var serializer = serviceStub.Serializer; Task <RpcResponse <TResponseReturn> > handler(TRequest request, GrpcCore.ServerCallContext context) { using (var serviceScope = CreateServiceScope(serviceStub)) { return(serviceStub.CallBlockingMethod( request, new GrpcCallContext(context), serviceCaller, responseConverter, faultHandler, serializer, serviceScope?.ServiceProvider).AsTask()); } } binder.AddMethod( GrpcMethodDefinition.Create <TRequest, RpcResponse <TResponseReturn> >(GrpcCore.MethodType.Unary, operationInfo.FullServiceName, operationInfo.Name, serializer), handler); }
protected abstract void AddGenericVoidBlockingMethodCore <TRequest>( Action <TService, TRequest, CancellationToken> serviceCaller, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, TMethodBinder binder) where TRequest : class, IObjectRequest;
protected abstract void AddGenericBlockingMethodCore <TRequest, TReturn, TResponseReturn>( Func <TService, TRequest, CancellationToken, TReturn> serviceCaller, Func <TReturn, TResponseReturn>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, TMethodBinder binder) where TRequest : class, IObjectRequest;
where TResponseReturn : class; // Needs to be class due to gRPC restriction protected abstract void AddCallbackMethodCore <TRequest, TReturn, TResponseReturn>( Func <TService, TRequest, Action <TReturn>, CancellationToken, Task> serviceCaller, Func <TReturn, TResponseReturn>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, TMethodBinder binder) where TRequest : class, IObjectRequest where TResponseReturn : class; // Needs to be class due to gRPC constraint
protected abstract void AddServerStreamingMethodCore <TRequest, TReturn, TResponseReturn>( Func <TService, TRequest, CancellationToken, IAsyncEnumerable <TReturn> > serviceCaller, Func <TReturn, TResponseReturn>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, TMethodBinder binder) where TRequest : class, IObjectRequest where TResponseReturn : class; // Needs to be class due to gRPC restriction
protected override void AddCallbackMethodCore <TRequest, TReturn, TResponse>( Func <TService, TRequest, Action <TReturn>, CancellationToken, Task> serviceCaller, Func <TReturn, TResponse>?responseConverter, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, ILightweightMethodBinder binder) { var serializer = serviceStub.Serializer; ValueTask HandleRequest(TRequest request, IServiceProvider?serviceProvider, IRpcAsyncStreamWriter <TResponse> responseWriter, LightweightCallContext context) => serviceStub.CallCallbackMethod(request, serviceProvider, context, responseWriter, serviceCaller, responseConverter, faultHandler, serializer); var methodStub = new LightweightStreamingMethodStub <TRequest, TResponse>(operationInfo.FullName, HandleRequest, serializer, faultHandler); binder.AddMethod(methodStub); }
protected override void AddGenericVoidBlockingMethodCore <TRequest>( Action <TService, TRequest, CancellationToken> serviceCaller, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, ILightweightMethodBinder binder) { var serializer = operationInfo.SerializerOverride ?? serviceStub.Serializer; ValueTask <RpcResponse> HandleRequest(TRequest request, IServiceProvider?serviceProvider, LightweightCallContext context) => serviceStub.CallVoidBlockingMethod(request, serviceProvider, context, serviceCaller, faultHandler, serializer); var methodStub = new LightweightMethodStub <TRequest, RpcResponse>(operationInfo.FullName, HandleRequest, serializer, faultHandler, operationInfo.AllowInlineExecution); binder.AddMethod(methodStub); }
private Type CreateRequestType(ModuleBuilder dynamicModuleBuilder, RpcOperationInfo opInfo) { try { var typeBuilder = dynamicModuleBuilder.DefineType($"{opInfo.Service.Name}_{opInfo.Name}Request"); var contractCtor = typeof(ProtoContractAttribute).GetConstructor(Type.EmptyTypes); var attribBuilder = new CustomAttributeBuilder(contractCtor, Array.Empty <object>()); typeBuilder.SetCustomAttribute(attribBuilder); var parameters = opInfo.Method.GetParameters(); int paramIndex = 0; foreach (var argType in opInfo.RequestParameters) { FieldBuilder fieldBuilder; if (paramIndex == 0) { fieldBuilder = typeBuilder.DefineField("Id", typeof(RpcObjectId), FieldAttributes.Public); } else { fieldBuilder = typeBuilder.DefineField(parameters[paramIndex - 1].Name, argType.Type, FieldAttributes.Public); } AddMemberAttribute(fieldBuilder, paramIndex + 1); paramIndex++; } return(typeBuilder.CreateType()); } catch (Exception e) { LogWarning(2, $"Failed to create request type for operation '{opInfo.FullName}'", e); } return(null); }
protected override void AddGenericVoidBlockingMethodCore <TRequest>( Action <TService, TRequest, CancellationToken> serviceCaller, RpcServerFaultHandler faultHandler, RpcStub <TService> serviceStub, RpcOperationInfo operationInfo, IGrpcMethodBinder binder) { var serializer = serviceStub.Serializer; GrpcCore.UnaryServerMethod <TRequest, RpcResponse> handler = (request, context) => { using (var serviceScope = CreateServiceScope(serviceStub)) { return(serviceStub.CallVoidBlockingMethod( request, serviceScope?.ServiceProvider, new GrpcCallContext(context), serviceCaller, faultHandler, serializer).AsTask()); } }; binder.AddMethod( GrpcMethodDefinition.Create <TRequest, RpcResponse>(GrpcCore.MethodType.Unary, operationInfo.FullServiceName, operationInfo.Name, serializer), handler); }
protected static Func <TService, TRequest, Action <TReturn>, CancellationToken, Task> GenerateBlockingCallbackMethodHandler <TRequest, TReturn>(RpcOperationInfo operationInfo) where TRequest : class { if (operationInfo is null) { throw new ArgumentNullException(nameof(operationInfo)); } var requestParameter = Expression.Parameter(typeof(TRequest)); var callbackParameter = Expression.Parameter(typeof(Action <TReturn>)); var cancellationTokenParameter = Expression.Parameter(typeof(CancellationToken)); List <Expression> parameterExpressions = GetParameterExpressions <TRequest>(operationInfo, requestParameter, callbackParameter, cancellationTokenParameter); var serviceParameter = Expression.Parameter(typeof(TService)); var invocation = Expression.Call(serviceParameter, operationInfo.Method, parameterExpressions); var expression = Expression.Lambda <Action <TService, TRequest, Action <TReturn>, CancellationToken> >( invocation, false, serviceParameter, requestParameter, callbackParameter, cancellationTokenParameter); var func = expression.Compile(); Task callFunc(TService service, TRequest request, Action <TReturn> callback, CancellationToken cancellation) { func(service, request, callback, cancellation); return(Task.CompletedTask); } return(callFunc); }
protected static Func <TService, TRequest, CancellationToken, Task <TResponse> > GenerateUnaryMethodHandler <TRequest, TResponse>(RpcOperationInfo operationInfo) where TRequest : class { if (operationInfo is null) { throw new ArgumentNullException(nameof(operationInfo)); } var requestParameter = Expression.Parameter(typeof(TRequest)); var cancellationTokenParameter = Expression.Parameter(typeof(CancellationToken)); List <Expression> parameterExpressions = GetParameterExpressions <TRequest>(operationInfo, requestParameter, cancellationTokenParameter); var serviceParameter = Expression.Parameter(typeof(TService)); var invocation = Expression.Call(serviceParameter, operationInfo.Method, parameterExpressions); var expression = Expression.Lambda <Func <TService, TRequest, CancellationToken, Task <TResponse> > >( invocation, false, serviceParameter, requestParameter, cancellationTokenParameter); var func = expression.Compile(); return(func); }
/// <summary> /// Enumerates all declared RPC members in the service interface specified by <paramref name="serviceInfo"/>. /// </summary> /// <param name="serviceInfo"></param> /// <param name="splitProperties">Indicates that separate <see cref="RpcOperationInfo"/>s should be returned for property get/set /// methods, instead of a single <see cref="RpcPropertyInfo"/>.</param> /// <returns></returns> // TODO: This method should maybe be moved to RpcServiceInfo, or at least be an RpcServiceInfo extension method. public static IEnumerable <RpcMemberInfo> EnumOperationHandlers(RpcServiceInfo serviceInfo, bool splitProperties) { var handledMembers = new HashSet <MemberInfo>(); var events = serviceInfo.Type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (var eventInfo in events) { if (eventInfo.EventHandlerType == null) { // How could this happen? throw new NotSupportedException($"{eventInfo.Name} has no EventHandlerType"); } if (eventInfo.AddMethod == null || eventInfo.RemoveMethod == null) { // How could this happen? throw new NotSupportedException($"{eventInfo.Name} is missing an Add or Remove method."); } var rpcEventInfo = GetEventInfoFromEvent(serviceInfo, eventInfo); handledMembers.Add(eventInfo.AddMethod); handledMembers.Add(eventInfo.RemoveMethod); yield return(rpcEventInfo); } var properties = serviceInfo.Type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); foreach (var propertyInfo in properties) { var rpcPropertyInfo = RpcBuilderUtil.GetPropertyInfoFromProperty(serviceInfo, propertyInfo); var propertyRpcAttribute = propertyInfo.GetCustomAttribute <RpcOperationAttribute>(); if (propertyInfo.GetMethod != null) { if (splitProperties) { var getRpcAttribute = propertyInfo.GetMethod.GetCustomAttribute <RpcOperationAttribute>(); var getOp = new RpcOperationInfo( service: rpcPropertyInfo.Service, name: $"Get{propertyInfo.Name}", declaringMember: propertyInfo, method: propertyInfo.GetMethod, requestType: typeof(RpcObjectRequest), requestParameters: ImmutableArray <RpcRequestParameter> .Empty, callbackParameterIndex: null, cancellationTokenIndex: null, methodType: RpcMethodType.Unary, isAsync: false, responseType: GetResponseType(RpcMethodType.Unary, rpcPropertyInfo.ResponseReturnType), responseReturnType: rpcPropertyInfo.ResponseReturnType, returnType: propertyInfo.PropertyType, returnKind: rpcPropertyInfo.PropertyTypeKind, allowInlineExecution: getRpcAttribute?.AllowInlineExecution ?? propertyRpcAttribute?.AllowInlineExecution ?? false, metadata: GetPropertyMethodMetadata(serviceInfo, propertyInfo, propertyInfo.GetMethod) ); yield return(getOp); } handledMembers.Add(propertyInfo.GetMethod); } if (propertyInfo.SetMethod != null) { if (splitProperties) { var setRpcAttribute = propertyInfo.SetMethod.GetCustomAttribute <RpcOperationAttribute>(); var setOp = new RpcOperationInfo( service: rpcPropertyInfo.Service, name: $"Set{propertyInfo.Name}", declaringMember: propertyInfo, method: propertyInfo.SetMethod, requestType: typeof(RpcObjectRequest <>).MakeGenericType(propertyInfo.PropertyType), requestParameters: ImmutableArray.Create( new RpcRequestParameter(propertyInfo.PropertyType, 0)), callbackParameterIndex: null, cancellationTokenIndex: null, methodType: RpcMethodType.Unary, isAsync: false, responseType: typeof(RpcResponse), returnType: typeof(void), responseReturnType: typeof(void), returnKind: ServiceOperationReturnKind.Standard, allowInlineExecution: setRpcAttribute?.AllowInlineExecution ?? propertyRpcAttribute?.AllowInlineExecution ?? false, metadata: GetPropertyMethodMetadata(serviceInfo, propertyInfo, propertyInfo.SetMethod) ); yield return(setOp); } handledMembers.Add(propertyInfo.SetMethod); } if (!splitProperties) { yield return(rpcPropertyInfo); } } foreach (var method in serviceInfo.Type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { if (handledMembers.Add(method)) { var opInfo = RpcBuilderUtil.GetOperationInfoFromMethod(serviceInfo, method); //this.CheckMethod(opInfo); switch (opInfo.MethodType) { case RpcMethodType.Unary: case RpcMethodType.ServerStreaming: yield return(opInfo); break; } } } }
protected void AddGenericCallbackMethod <TRequest, TReturn, TResponseReturn>(RpcStub <TService> serviceStub, RpcOperationInfo opInfo, TMethodBinder binder) where TRequest : class, IObjectRequest where TResponseReturn : class // Needs to be class due to gRPC constraint { if (opInfo is null) { throw new ArgumentNullException(nameof(opInfo)); } Func <TReturn, TResponseReturn>?responseCreator = GetResponseCreator <TReturn, TResponseReturn>(opInfo); RpcServerFaultHandler faultHandler = this.CreateFaultHandler(opInfo); if (opInfo.IsAsync) { var serviceCaller = GenerateCallbackMethodHandler <TRequest, TReturn>(opInfo); this.AddCallbackMethodCore(serviceCaller, responseCreator, faultHandler, serviceStub, opInfo, binder); } else { var serviceCaller = GenerateBlockingCallbackMethodHandler <TRequest, TReturn>(opInfo); this.AddCallbackMethodCore(serviceCaller, responseCreator, faultHandler, serviceStub, opInfo, binder); } }