public ServiceContext(Type serviceType, MethodInfo methodInfo, ILookup <Type, Attribute> attributeLookup, MethodType methodType, ServerCallContext context, IFormatterResolver resolver, IMagicOnionLogger logger, MethodHandler methodHandler, IServiceLocator serviceLocator, IMagicOnionServiceActivator activator) { this.ContextId = Guid.NewGuid(); this.ServiceType = serviceType; this.MethodInfo = methodInfo; this.AttributeLookup = attributeLookup; this.MethodType = methodType; this.CallContext = context; this.Timestamp = DateTime.UtcNow; this.FormatterResolver = resolver; this.MagicOnionLogger = logger; this.MethodHandler = methodHandler; this.ServiceLocator = serviceLocator; this.ServiceActivator = activator; }
public StreamingHubHandler(Type classType, MethodInfo methodInfo, StreamingHubHandlerOptions handlerOptions) { var hubInterface = classType.GetInterfaces().First(x => x.GetTypeInfo().IsGenericType&& x.GetGenericTypeDefinition() == typeof(IStreamingHub <,>)).GetGenericArguments()[0]; var interfaceMethod = GetInterfaceMethod(classType, hubInterface, methodInfo.Name); this.HubType = classType; this.HubName = hubInterface.Name; this.MethodInfo = methodInfo; // Validation for Id if (methodInfo.GetCustomAttribute <MethodIdAttribute>() != null) { throw new InvalidOperationException($"Hub Implementation can not add [MethodId], you should add hub `interface`. {classType.Name}/{methodInfo.Name}"); } this.MethodId = interfaceMethod.GetCustomAttribute <MethodIdAttribute>()?.MethodId ?? FNV1A32.GetHashCode(interfaceMethod.Name); this.UnwrappedResponseType = UnwrapResponseType(methodInfo); var resolver = handlerOptions.SerializerOptions.Resolver; var parameters = methodInfo.GetParameters(); if (RequestType == null) { this.RequestType = MagicOnionMarshallers.CreateRequestTypeAndSetResolver(classType.Name + "/" + methodInfo.Name, parameters, ref resolver); } this.serializerOptions = handlerOptions.SerializerOptions.WithResolver(resolver); this.AttributeLookup = classType.GetCustomAttributes(true) .Concat(methodInfo.GetCustomAttributes(true)) .Cast <Attribute>() .ToLookup(x => x.GetType()); this.filters = handlerOptions.GlobalStreamingHubFilters .OfType <IMagicOnionFilterFactory <StreamingHubFilterAttribute> >() .Concat(classType.GetCustomAttributes <StreamingHubFilterAttribute>(true).Select(x => new StreamingHubFilterDescriptor(x, x.Order))) .Concat(classType.GetCustomAttributes(true).OfType <IMagicOnionFilterFactory <StreamingHubFilterAttribute> >()) .Concat(methodInfo.GetCustomAttributes <StreamingHubFilterAttribute>(true).Select(x => new StreamingHubFilterDescriptor(x, x.Order))) .Concat(methodInfo.GetCustomAttributes(true).OfType <IMagicOnionFilterFactory <StreamingHubFilterAttribute> >()) .OrderBy(x => x.Order) .ToArray(); // options this.serviceLocator = handlerOptions.ServiceLocator; this.serviceActivator = handlerOptions.ServiceActivator; // validation filter if (methodInfo.GetCustomAttribute <MagicOnionFilterAttribute>(true) != null) { throw new InvalidOperationException($"StreamingHub method can not add [MagicOnionFilter], you should add [StreamingHubFilter]. {classType.Name}/{methodInfo.Name}"); } this.toStringCache = HubName + "/" + MethodInfo.Name; this.getHashCodeCache = HubName.GetHashCode() ^ MethodInfo.Name.GetHashCode() << 2; // ValueTask (StreamingHubContext context) => // { // T request = LZ4MessagePackSerializer.Deserialize<T>(context.Request, context.FormatterResolver); // Task<T> result = ((HubType)context.HubInstance).Foo(request); // return WriteInAsyncLockInTaskWithMessageId(result) || return new ValueTask(result) // } try { var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; var contextArg = Expression.Parameter(typeof(StreamingHubContext), "context"); var requestArg = Expression.Parameter(RequestType, "request"); var getSerializerOptions = Expression.Property(contextArg, typeof(StreamingHubContext).GetProperty("SerializerOptions", flags)); var contextRequest = Expression.Property(contextArg, typeof(StreamingHubContext).GetProperty("Request", flags)); var noneCancellation = Expression.Default(typeof(CancellationToken)); var getInstanceCast = Expression.Convert(Expression.Property(contextArg, typeof(StreamingHubContext).GetProperty("HubInstance", flags)), HubType); var callDeserialize = Expression.Call(messagePackDeserialize.MakeGenericMethod(RequestType), contextRequest, getSerializerOptions, noneCancellation); var assignRequest = Expression.Assign(requestArg, callDeserialize); Expression[] arguments = new Expression[parameters.Length]; if (parameters.Length == 1) { arguments[0] = requestArg; } else { for (int i = 0; i < parameters.Length; i++) { arguments[i] = Expression.Field(requestArg, "Item" + (i + 1)); } } var callBody = Expression.Call(getInstanceCast, methodInfo, arguments); var finalMethod = (methodInfo.ReturnType.IsGenericType) ? typeof(StreamingHubContext).GetMethod(nameof(StreamingHubContext.WriteResponseMessage), flags).MakeGenericMethod(UnwrappedResponseType) : typeof(StreamingHubContext).GetMethod(nameof(StreamingHubContext.WriteResponseMessageNil), flags); callBody = Expression.Call(contextArg, finalMethod, callBody); var body = Expression.Block(new[] { requestArg }, assignRequest, callBody); var compiledBody = Expression.Lambda(body, contextArg).Compile(); this.MethodBody = BuildMethodBodyWithFilter((Func <StreamingHubContext, ValueTask>)compiledBody); } catch (Exception ex) { throw new InvalidOperationException($"Can't create handler. Path:{ToString()}", ex); } }
public MethodHandler(Type classType, MethodInfo methodInfo, string methodName, MethodHandlerOptions handlerOptions) { this.methodHandlerId = Interlocked.Increment(ref methodHandlerIdBuild); var serviceInterfaceType = classType.GetInterfaces().First(x => x.GetTypeInfo().IsGenericType&& x.GetGenericTypeDefinition() == typeof(IService <>)).GetGenericArguments()[0]; this.ServiceType = classType; this.ServiceName = serviceInterfaceType.Name; this.MethodInfo = methodInfo; this.MethodName = methodName; MethodType mt; this.UnwrappedResponseType = UnwrapResponseType(methodInfo, out mt, out responseIsTask, out this.RequestType); this.MethodType = mt; this.serializerOptions = handlerOptions.SerializerOptions; var parameters = methodInfo.GetParameters(); if (RequestType == null) { var resolver = this.serializerOptions.Resolver; this.RequestType = MagicOnionMarshallers.CreateRequestTypeAndSetResolver(classType.Name + "/" + methodInfo.Name, parameters, ref resolver); this.serializerOptions = this.serializerOptions.WithResolver(resolver); } this.AttributeLookup = classType.GetCustomAttributes(true) .Concat(methodInfo.GetCustomAttributes(true)) .Cast <Attribute>() .ToLookup(x => x.GetType()); this.filters = handlerOptions.GlobalFilters .OfType <IMagicOnionFilterFactory <MagicOnionFilterAttribute> >() .Concat(classType.GetCustomAttributes <MagicOnionFilterAttribute>(true).Select(x => new MagicOnionServiceFilterDescriptor(x, x.Order))) .Concat(classType.GetCustomAttributes(true).OfType <IMagicOnionFilterFactory <MagicOnionFilterAttribute> >()) .Concat(methodInfo.GetCustomAttributes <MagicOnionFilterAttribute>(true).Select(x => new MagicOnionServiceFilterDescriptor(x, x.Order))) .Concat(methodInfo.GetCustomAttributes(true).OfType <IMagicOnionFilterFactory <MagicOnionFilterAttribute> >()) .OrderBy(x => x.Order) .ToArray(); // options this.isReturnExceptionStackTraceInErrorDetail = handlerOptions.IsReturnExceptionStackTraceInErrorDetail; this.logger = handlerOptions.Logger; this.enableCurrentContext = handlerOptions.EnableCurrentContext; this.serviceLocator = handlerOptions.ServiceLocator; this.serviceActivator = handlerOptions.ServiceActivator; // prepare lambda parameters var createServiceMethodInfo = createService.MakeGenericMethod(classType, serviceInterfaceType); var contextArg = Expression.Parameter(typeof(ServiceContext), "context"); var instance = Expression.Call(createServiceMethodInfo, contextArg); switch (MethodType) { case MethodType.Unary: case MethodType.ServerStreaming: // (ServiceContext context) => // { // var request = MessagePackSerializer.Deserialize<T>(context.Request, context.SerializerOptions, default); // var result = new FooService() { Context = context }.Bar(request.Item1, request.Item2); // return MethodHandlerResultHelper.SerializeUnaryResult(result, context); // }; try { var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; var staticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; var requestArg = Expression.Parameter(RequestType, "request"); var getSerializerOptions = Expression.Property(contextArg, typeof(ServiceContext).GetProperty("SerializerOptions", flags)); var defaultToken = Expression.Default(typeof(CancellationToken)); var contextRequest = Expression.Property(contextArg, typeof(ServiceContext).GetProperty("Request", flags)); var callDeserialize = Expression.Call(messagePackDeserialize.MakeGenericMethod(RequestType), contextRequest, getSerializerOptions, defaultToken); var assignRequest = Expression.Assign(requestArg, callDeserialize); Expression[] arguments = new Expression[parameters.Length]; if (parameters.Length == 1) { arguments[0] = requestArg; } else { for (int i = 0; i < parameters.Length; i++) { arguments[i] = Expression.Field(requestArg, "Item" + (i + 1)); } } var callBody = Expression.Call(instance, methodInfo, arguments); if (MethodType == MethodType.Unary) { var finalMethod = (responseIsTask) ? typeof(MethodHandlerResultHelper).GetMethod("SerializeTaskUnaryResult", staticFlags).MakeGenericMethod(UnwrappedResponseType) : typeof(MethodHandlerResultHelper).GetMethod("SerializeUnaryResult", staticFlags).MakeGenericMethod(UnwrappedResponseType); callBody = Expression.Call(finalMethod, callBody, contextArg); } else { if (!responseIsTask) { callBody = Expression.Call(typeof(MethodHandlerResultHelper) .GetMethod(nameof(MethodHandlerResultHelper.NewEmptyValueTask), staticFlags) .MakeGenericMethod(MethodInfo.ReturnType), callBody); } else { callBody = Expression.Call(typeof(MethodHandlerResultHelper) .GetMethod(nameof(MethodHandlerResultHelper.TaskToEmptyValueTask), staticFlags) .MakeGenericMethod(MethodInfo.ReturnType.GetGenericArguments()[0]), callBody); } } var body = Expression.Block(new[] { requestArg }, assignRequest, callBody); var compiledBody = Expression.Lambda(body, contextArg).Compile(); this.methodBody = BuildMethodBodyWithFilter((Func <ServiceContext, ValueTask>)compiledBody); } catch (Exception ex) { throw new InvalidOperationException($"Can't create handler. Path:{ToString()}", ex); } break; case MethodType.ClientStreaming: case MethodType.DuplexStreaming: if (parameters.Length != 0) { throw new InvalidOperationException($"{MethodType} does not support method parameters. If you need to send initial parameter, use header instead. Path:{ToString()}"); } // (ServiceContext context) => new FooService() { Context = context }.Bar(); try { var body = Expression.Call(instance, methodInfo); var staticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; if (MethodType == MethodType.ClientStreaming) { var finalMethod = (responseIsTask) ? typeof(MethodHandlerResultHelper).GetMethod("SerializeTaskClientStreamingResult", staticFlags).MakeGenericMethod(RequestType, UnwrappedResponseType) : typeof(MethodHandlerResultHelper).GetMethod("SerializeClientStreamingResult", staticFlags).MakeGenericMethod(RequestType, UnwrappedResponseType); body = Expression.Call(finalMethod, body, contextArg); } else { if (!responseIsTask) { body = Expression.Call(typeof(MethodHandlerResultHelper) .GetMethod(nameof(MethodHandlerResultHelper.NewEmptyValueTask), staticFlags) .MakeGenericMethod(MethodInfo.ReturnType), body); } else { body = Expression.Call(typeof(MethodHandlerResultHelper) .GetMethod(nameof(MethodHandlerResultHelper.TaskToEmptyValueTask), staticFlags) .MakeGenericMethod(MethodInfo.ReturnType.GetGenericArguments()[0]), body); } } var compiledBody = Expression.Lambda(body, contextArg).Compile(); this.methodBody = BuildMethodBodyWithFilter((Func <ServiceContext, ValueTask>)compiledBody); } catch (Exception ex) { throw new InvalidOperationException($"Can't create handler. Path:{ToString()}", ex); } break; default: throw new InvalidOperationException("Unknown MethodType:" + MethodType + $"Path:{ToString()}"); } }