예제 #1
0
 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;
 }
예제 #2
0
        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);
            }
        }
예제 #3
0
        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()}");
            }
        }