Beispiel #1
0
 public MethodHandlerOptions(MagicOnionOptions options)
 {
     GlobalFilters = options.GlobalFilters;
     IsReturnExceptionStackTraceInErrorDetail = options.IsReturnExceptionStackTraceInErrorDetail;
     EnableCurrentContext = options.EnableCurrentContext;
     SerializerOptions    = options.SerializerOptions;
 }
Beispiel #2
0
 public MethodHandlerOptions(MagicOnionOptions options)
 {
     GlobalFilters = options.GlobalFilters;
     IsReturnExceptionStackTraceInErrorDetail = options.IsReturnExceptionStackTraceInErrorDetail;
     EnableCurrentContext = options.EnableCurrentContext;
     Logger            = options.MagicOnionLogger;
     FormatterResolver = options.FormatterResolver;
     ServiceLocator    = options.ServiceLocator;
     ServiceActivator  = options.MagicOnionServiceActivator;
 }
        static IEnumerable <Type> SupplyEmbeddedServices(MagicOnionOptions options)
        {
            if (options.DisableEmbeddedService)
            {
                yield break;
            }

            yield return(typeof(MagicOnion.Server.EmbeddedServices.MagicOnionEmbeddedHeartbeat));

            yield return(typeof(MagicOnion.Server.EmbeddedServices.MagicOnionEmbeddedPing));
        }
        public static MagicOnionServiceDefinition BuildServerServiceDefinition(IEnumerable <Type> targetTypes, MagicOnionOptions option)
        {
            option.RegisterOptionToServiceLocator();

            var builder              = ServerServiceDefinition.CreateBuilder();
            var handlers             = new HashSet <MethodHandler>();
            var streamingHubHandlers = new List <StreamingHubHandler>();

            var types = targetTypes
                        .Where(x => typeof(IServiceMarker).IsAssignableFrom(x))
                        .Where(x => !x.GetTypeInfo().IsAbstract)
                        .Where(x => x.GetCustomAttribute <IgnoreAttribute>(false) == null)
                        .Concat(SupplyEmbeddedServices(option))
                        .ToArray();

            option.MagicOnionLogger.BeginBuildServiceDefinition();
            var sw = Stopwatch.StartNew();

            try
            {
                Parallel.ForEach(types, /* new ParallelOptions { MaxDegreeOfParallelism = 1 }, */ classType =>
                {
                    var className = classType.Name;
                    if (!classType.GetConstructors().Any(x => x.GetParameters().Length == 0))
                    {
                        throw new InvalidOperationException(string.Format("Type needs parameterless constructor, class:{0}", classType.FullName));
                    }

                    var isStreamingHub = typeof(IStreamingHubMarker).IsAssignableFrom(classType);
                    HashSet <StreamingHubHandler> tempStreamingHubHandlers = null;
                    if (isStreamingHub)
                    {
                        tempStreamingHubHandlers = new HashSet <StreamingHubHandler>();
                    }

                    var inheritInterface = classType.GetInterfaces()
                                           .First(x => x.IsGenericType && x.GetGenericTypeDefinition() == (isStreamingHub ? typeof(IStreamingHub <,>) : typeof(IService <>)))
                                           .GenericTypeArguments[0];
                    var interfaceMap = classType.GetInterfaceMap(inheritInterface);

                    for (int i = 0; i < interfaceMap.TargetMethods.Length; ++i)
                    {
                        var methodInfo = interfaceMap.TargetMethods[i];
                        var methodName = interfaceMap.InterfaceMethods[i].Name;

                        if (methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_") || methodInfo.Name.StartsWith("get_")))
                        {
                            continue;
                        }
                        if (methodInfo.GetCustomAttribute <IgnoreAttribute>(false) != null)
                        {
                            continue;                                                                // ignore
                        }
                        // ignore default methods
                        if (methodName == "Equals" ||
                            methodName == "GetHashCode" ||
                            methodName == "GetType" ||
                            methodName == "ToString" ||
                            methodName == "WithOptions" ||
                            methodName == "WithHeaders" ||
                            methodName == "WithDeadline" ||
                            methodName == "WithCancellationToken" ||
                            methodName == "WithHost"
                            )
                        {
                            continue;
                        }

                        // register for StreamingHub
                        if (isStreamingHub && methodName != "Connect")
                        {
                            var streamingHandler = new StreamingHubHandler(option, classType, methodInfo);
                            if (!tempStreamingHubHandlers.Add(streamingHandler))
                            {
                                throw new InvalidOperationException($"Method does not allow overload, {className}.{methodName}");
                            }
                            continue;
                        }
                        else
                        {
                            // create handler
                            var handler = new MethodHandler(option, classType, methodInfo, methodName);
                            lock (builder)
                            {
                                if (!handlers.Add(handler))
                                {
                                    throw new InvalidOperationException($"Method does not allow overload, {className}.{methodName}");
                                }
                                handler.RegisterHandler(builder);
                            }
                        }
                    }

                    if (isStreamingHub)
                    {
                        var connectHandler = new MethodHandler(option, classType, classType.GetMethod("Connect"), "Connect");
                        lock (builder)
                        {
                            if (!handlers.Add(connectHandler))
                            {
                                throw new InvalidOperationException($"Method does not allow overload, {className}.Connect");
                            }
                            connectHandler.RegisterHandler(builder);
                        }

                        lock (streamingHubHandlers)
                        {
                            streamingHubHandlers.AddRange(tempStreamingHubHandlers);
                            StreamingHubHandlerRepository.RegisterHandler(connectHandler, tempStreamingHubHandlers.ToArray());
                            IGroupRepositoryFactory factory;
                            var attr = classType.GetCustomAttribute <GroupConfigurationAttribute>(true);
                            if (attr != null)
                            {
                                factory = attr.Create();
                            }
                            else
                            {
                                factory = option.DefaultGroupRepositoryFactory;
                            }
                            StreamingHubHandlerRepository.AddGroupRepository(connectHandler, factory.CreateRepository(option.ServiceLocator));
                        }
                    }
                });
            }
            catch (AggregateException agex)
            {
                ExceptionDispatchInfo.Capture(agex.InnerExceptions[0]).Throw();
            }

            var result = new MagicOnionServiceDefinition(builder.Build(), handlers.ToArray(), streamingHubHandlers.ToArray());

            sw.Stop();
            option.MagicOnionLogger.EndBuildServiceDefinition(sw.Elapsed.TotalMilliseconds);

            return(result);
        }
        /// <summary>
        /// Search MagicOnion service from target assemblies. ex: new[]{ typeof(Startup).GetTypeInfo().Assembly }
        /// </summary>
        public static MagicOnionServiceDefinition BuildServerServiceDefinition(Assembly[] searchAssemblies, MagicOnionOptions option)
        {
            var types = searchAssemblies
                        .SelectMany(x =>
            {
                try
                {
                    return(x.GetTypes());
                }
                catch (ReflectionTypeLoadException ex)
                {
                    return(ex.Types.Where(t => t != null));
                }
            });

            return(BuildServerServiceDefinition(types, option));
        }
 /// <summary>
 /// Search MagicOnion service from all assemblies.
 /// </summary>
 public static MagicOnionServiceDefinition BuildServerServiceDefinition(MagicOnionOptions options)
 {
     return(BuildServerServiceDefinition(AppDomain.CurrentDomain.GetAssemblies(), options));
 }
        public static MagicOnionServiceDefinition BuildServerServiceDefinition(IEnumerable <Type> targetTypes, MagicOnionOptions option)
        {
            var builder  = ServerServiceDefinition.CreateBuilder();
            var handlers = new HashSet <MethodHandler>();

            var types = targetTypes
                        .Where(x => typeof(IServiceMarker).IsAssignableFrom(x))
                        .Where(x => !x.IsAbstract)
                        .Where(x => x.GetCustomAttribute <IgnoreAttribute>(false) == null)
                        .Concat(SupplyEmbeddedServices(option))
                        .ToArray();

            option.MagicOnionLogger.BeginBuildServiceDefinition();
            var sw = Stopwatch.StartNew();

            Parallel.ForEach(types, /* new ParallelOptions { MaxDegreeOfParallelism = 1 },*/ classType =>
            {
                var className = classType.Name;
                if (!classType.GetConstructors().Any(x => x.GetParameters().Length == 0))
                {
                    throw new InvalidOperationException(string.Format("Type needs parameterless constructor, class:{0}", classType.FullName));
                }

                foreach (var methodInfo in classType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
                {
                    if (methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_") || methodInfo.Name.StartsWith("get_")))
                    {
                        continue;
                    }
                    if (methodInfo.GetCustomAttribute <IgnoreAttribute>(false) != null)
                    {
                        continue;                                                                // ignore
                    }
                    var methodName = methodInfo.Name;

                    // ignore default methods
                    if (methodName == "Equals" ||
                        methodName == "GetHashCode" ||
                        methodName == "GetType" ||
                        methodName == "ToString" ||
                        methodName == "WithOptions" ||
                        methodName == "WithHeaders" ||
                        methodName == "WithDeadline" ||
                        methodName == "WithCancellationToken" ||
                        methodName == "WithHost"
                        )
                    {
                        continue;
                    }

                    // create handler
                    var handler = new MethodHandler(option, classType, methodInfo);
                    lock (builder)
                    {
                        if (!handlers.Add(handler))
                        {
                            throw new InvalidOperationException($"Method does not allow overload, {className}.{methodName}");
                        }
                        handler.RegisterHandler(builder);
                    }
                }
            });

            var result = new MagicOnionServiceDefinition(builder.Build(), handlers.ToArray());

            sw.Stop();
            option.MagicOnionLogger.EndBuildServiceDefinition(sw.Elapsed.TotalMilliseconds);

            return(result);
        }
Beispiel #8
0
        public MethodHandler(MagicOnionOptions options, Type classType, MethodInfo methodInfo)
        {
            this.ServiceType = classType;
            this.ServiceName = classType.GetInterfaces().First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IService <>)).GetGenericArguments()[0].Name;
            this.MethodInfo  = methodInfo;
            MethodType mt;

            this.UnwrappedResponseType = UnwrapResponseType(methodInfo, out mt, out responseIsTask, out this.RequestType);
            this.MethodType            = mt;
            this.resolver = options.FormatterResolver;

            var parameters = methodInfo.GetParameters();

            if (RequestType == null)
            {
                this.RequestType = MagicOnionMarshallers.CreateRequestTypeAndSetResolver(classType.Name + "/" + methodInfo.Name, parameters, ref resolver);
            }

            this.AttributeLookup = classType.GetCustomAttributes(true)
                                   .Concat(methodInfo.GetCustomAttributes(true))
                                   .Cast <Attribute>()
                                   .ToLookup(x => x.GetType());

            this.filters = options.GlobalFilters
                           .Concat(classType.GetCustomAttributes <MagicOnionFilterAttribute>(true))
                           .Concat(methodInfo.GetCustomAttributes <MagicOnionFilterAttribute>(true))
                           .OrderBy(x => x.Order)
                           .ToArray();

            // options
            this.isReturnExceptionStackTraceInErrorDetail = options.IsReturnExceptionStackTraceInErrorDetail;
            this.logger = options.MagicOnionLogger;

            // prepare lambda parameters
            var contextArg  = Expression.Parameter(typeof(ServiceContext), "context");
            var contextBind = Expression.Bind(classType.GetProperty("Context"), contextArg);
            var instance    = Expression.MemberInit(Expression.New(classType), contextBind);

            switch (MethodType)
            {
            case MethodType.Unary:
            case MethodType.ServerStreaming:
                // (ServiceContext context) =>
                // {
                //      var request = LZ4MessagePackSerializer.Deserialize<T>(context.Request, context.Resolver);
                //      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 getResolver = Expression.Property(contextArg, typeof(ServiceContext).GetProperty("FormatterResolver", flags));

                    var contextRequest = Expression.Property(contextArg, typeof(ServiceContext).GetProperty("Request", flags));

                    var callDeserialize = Expression.Call(messagePackDeserialize.MakeGenericMethod(RequestType), contextRequest, getResolver);
                    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(Task).GetMethod("FromResult").MakeGenericMethod(MethodInfo.ReturnType), callBody);
                        }
                    }

                    var body         = Expression.Block(new[] { requestArg }, assignRequest, callBody);
                    var compiledBody = Expression.Lambda(body, contextArg).Compile();

                    this.methodBody = BuildMethodBodyWithFilter((Func <ServiceContext, Task>)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);

                    if (MethodType == MethodType.ClientStreaming)
                    {
                        var staticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
                        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(Task).GetMethod("FromResult").MakeGenericMethod(MethodInfo.ReturnType), body);
                        }
                    }

                    var compiledBody = Expression.Lambda(body, contextArg).Compile();

                    this.methodBody = BuildMethodBodyWithFilter((Func <ServiceContext, Task>)compiledBody);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException($"Can't create handler. Path:{ToString()}", ex);
                }
                break;

            default:
                throw new InvalidOperationException("Unknown MethodType:" + MethodType + $"Path:{ToString()}");
            }
        }
Beispiel #9
0
        public MethodHandler(MagicOnionOptions options, Type classType, MethodInfo methodInfo)
        {
            this.ServiceType = classType;
            this.ServiceName = classType.GetInterfaces().First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IService <>)).GetGenericArguments()[0].Name;
            this.MethodInfo  = methodInfo;
            MethodType mt;

            this.UnwrappedResponseType = UnwrapResponseType(methodInfo, out mt, out responseIsTask, out this.RequestType);
            this.MethodType            = mt;

            var parameters = methodInfo.GetParameters();

            if (RequestType == null)
            {
                this.RequestType = MagicOnionMarshallers.CreateRequestTypeAndMarshaller(options.ZeroFormatterTypeResolverType, classType.Name + "/" + methodInfo.Name, parameters, out requestMarshaller);
            }
            else
            {
                this.requestMarshaller = MagicOnionMarshallers.CreateZeroFormattertMarshallerReflection(options.ZeroFormatterTypeResolverType, RequestType);
            }

            this.responseMarshaller = MagicOnionMarshallers.CreateZeroFormattertMarshallerReflection(options.ZeroFormatterTypeResolverType, UnwrappedResponseType);

            this.AttributeLookup = classType.GetCustomAttributes(true)
                                   .Concat(methodInfo.GetCustomAttributes(true))
                                   .Cast <Attribute>()
                                   .ToLookup(x => x.GetType());

            this.filters = options.GlobalFilters
                           .Concat(classType.GetCustomAttributes <MagicOnionFilterAttribute>(true))
                           .Concat(methodInfo.GetCustomAttributes <MagicOnionFilterAttribute>(true))
                           .OrderBy(x => x.Order)
                           .ToArray();

            // options
            this.isReturnExceptionStackTraceInErrorDetail = options.IsReturnExceptionStackTraceInErrorDetail;
            this.logger = options.MagicOnionLogger;

            // prepare lambda parameters
            var contextArg  = Expression.Parameter(typeof(ServiceContext), "context");
            var contextBind = Expression.Bind(classType.GetProperty("Context"), contextArg);
            var instance    = Expression.MemberInit(Expression.New(classType), contextBind);

            switch (MethodType)
            {
            case MethodType.Unary:
            case MethodType.ServerStreaming:
                // (ServiceContext context) =>
                // {
                //      var request = ((Marshaller<TRequest>)context.RequestMarshaller).Deserializer.Invoke(context.Request);
                //      return new FooService() { Context = context }.Bar(request.Item1, request.Item2);
                // };
            {
                var flags          = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
                var marshallerType = typeof(Marshaller <>).MakeGenericType(RequestType);

                var requestArg = Expression.Parameter(RequestType, "request");

                var requestMarshalleExpr = Expression.Convert(Expression.Property(contextArg, typeof(ServiceContext).GetProperty("RequestMarshaller", flags)), marshallerType);
                var deserializer         = Expression.Property(requestMarshalleExpr, "Deserializer");
                var callDeserialize      = Expression.Call(deserializer, typeof(Func <,>).MakeGenericType(typeof(byte[]), RequestType).GetMethod("Invoke"), Expression.Property(contextArg, typeof(ServiceContext).GetProperty("Request", flags)));

                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 (!responseIsTask)
                {
                    callBody = Expression.Call(typeof(Task).GetMethod("FromResult").MakeGenericMethod(MethodInfo.ReturnType), callBody);
                }

                var body         = Expression.Block(new[] { requestArg }, assignRequest, callBody);
                var compiledBody = Expression.Lambda(body, contextArg).Compile();

                this.methodBody = BuildMethodBodyWithFilter((Func <ServiceContext, Task>)compiledBody);
            }
            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.");
                }

                // (ServiceContext context) => new FooService() { Context = context }.Bar();
                {
                    var body = Expression.Call(instance, methodInfo);
                    if (!responseIsTask)
                    {
                        body = Expression.Call(typeof(Task).GetMethod("FromResult").MakeGenericMethod(MethodInfo.ReturnType), body);
                    }

                    var compiledBody = Expression.Lambda(body, contextArg).Compile();

                    this.methodBody = BuildMethodBodyWithFilter((Func <ServiceContext, Task>)compiledBody);
                }
                break;

            default:
                throw new InvalidOperationException("Unknown MethodType:" + MethodType);
            }

            // Utility
            {
                // (object requestMarshaller, object value) => ((Marshaller<TRequest>)requestMarshaller).Serializer.Invoke((TRequest)value);
                var marshallerType       = typeof(Marshaller <>).MakeGenericType(RequestType);
                var requestMarshallerArg = Expression.Parameter(typeof(object), "requestMarshaller");
                var valueArg             = Expression.Parameter(typeof(object), "value");
                var serializer           = Expression.Property(Expression.Convert(requestMarshallerArg, marshallerType), "Serializer");
                var callSerialize        = Expression.Call(serializer, typeof(Func <,>).MakeGenericType(RequestType, typeof(byte[])).GetMethod("Invoke"),
                                                           Expression.Convert(valueArg, RequestType));

                boxedRequestSerialize = Expression.Lambda <Func <object, object, byte[]> >(callSerialize, requestMarshallerArg, valueArg).Compile();
            }
            {
                // (object responseMarshaller, byte[] value) => ((Marshaller<TResponse>)requestMarshaller).Deserializer.Invoke(value);
                var marshallerType        = typeof(Marshaller <>).MakeGenericType(UnwrappedResponseType);
                var responseMarshallerArg = Expression.Parameter(typeof(object), "responseMarshaller");
                var valueArg        = Expression.Parameter(typeof(byte[]), "value");
                var deserializer    = Expression.Property(Expression.Convert(responseMarshallerArg, marshallerType), "Deserializer");
                var callDeserialize = Expression.Convert(Expression.Call(deserializer, typeof(Func <,>).MakeGenericType(typeof(byte[]), UnwrappedResponseType).GetMethod("Invoke"), valueArg), typeof(object));
                boxedResponseDeserialize = Expression.Lambda <Func <object, byte[], object> >(callDeserialize, responseMarshallerArg, valueArg).Compile();
            }
        }
 /// <summary>
 /// Search MagicOnion service from entry assembly.
 /// </summary>
 public static MagicOnionServiceDefinition BuildServerServiceDefinition(MagicOnionOptions options)
 {
     return(BuildServerServiceDefinition(new[] { Assembly.GetEntryAssembly() }, options));
 }
Beispiel #11
0
        /// <summary>
        /// Search MagicOnion service from target types.
        /// </summary>
        /// <param name="serviceProvider">The service provider is used to resolve dependencies</param>
        /// <param name="targetTypes">The types to be search for services</param>
        /// <param name="options">The options for MagicOnion server</param>
        public static MagicOnionServiceDefinition BuildServerServiceDefinition(IServiceProvider serviceProvider, IEnumerable <Type> targetTypes, MagicOnionOptions options)
        {
            var handlers             = new HashSet <MethodHandler>();
            var streamingHubHandlers = new List <StreamingHubHandler>();

            var types = targetTypes
                        .Where(x => typeof(IServiceMarker).IsAssignableFrom(x))
                        .Where(x => !x.GetTypeInfo().IsAbstract)
                        .Where(x => x.GetCustomAttribute <IgnoreAttribute>(false) == null)
                        .ToArray();

            var logger = serviceProvider.GetRequiredService <IMagicOnionLogger>();

            logger.BeginBuildServiceDefinition();
            var sw = Stopwatch.StartNew();

            try
            {
                foreach (var classType in types)
                {
                    var className = classType.Name;
                    if (!classType.GetConstructors().Any(x => x.GetParameters().Length == 0))
                    {
                        // supports paramaterless constructor after v2.1(DI support).
                        // throw new InvalidOperationException(string.Format("Type needs parameterless constructor, class:{0}", classType.FullName));
                    }

                    var isStreamingHub = typeof(IStreamingHubMarker).IsAssignableFrom(classType);
                    HashSet <StreamingHubHandler>?tempStreamingHubHandlers = null;
                    if (isStreamingHub)
                    {
                        tempStreamingHubHandlers = new HashSet <StreamingHubHandler>();
                    }

                    var inheritInterface = classType.GetInterfaces()
                                           .First(x => x.IsGenericType && x.GetGenericTypeDefinition() == (isStreamingHub ? typeof(IStreamingHub <,>) : typeof(IService <>)))
                                           .GenericTypeArguments[0];

                    if (!inheritInterface.IsAssignableFrom(classType))
                    {
                        throw new NotImplementedException($"Type '{classType.FullName}' has no implementation of interface '{inheritInterface.FullName}'.");
                    }

                    var interfaceMap = classType.GetInterfaceMap(inheritInterface);

                    for (int i = 0; i < interfaceMap.TargetMethods.Length; ++i)
                    {
                        var methodInfo = interfaceMap.TargetMethods[i];
                        var methodName = interfaceMap.InterfaceMethods[i].Name;

                        if (methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_") || methodInfo.Name.StartsWith("get_")))
                        {
                            continue;
                        }
                        if (methodInfo.GetCustomAttribute <IgnoreAttribute>(false) != null)
                        {
                            continue;                                                                // ignore
                        }
                        // ignore default methods
                        if (methodName == "Equals" ||
                            methodName == "GetHashCode" ||
                            methodName == "GetType" ||
                            methodName == "ToString" ||
                            methodName == "WithOptions" ||
                            methodName == "WithHeaders" ||
                            methodName == "WithDeadline" ||
                            methodName == "WithCancellationToken" ||
                            methodName == "WithHost"
                            )
                        {
                            continue;
                        }

                        // register for StreamingHub
                        if (isStreamingHub && methodName != "Connect")
                        {
                            var streamingHandler = new StreamingHubHandler(classType, methodInfo, new StreamingHubHandlerOptions(options), serviceProvider);
                            if (!tempStreamingHubHandlers !.Add(streamingHandler))
                            {
                                throw new InvalidOperationException($"Method does not allow overload, {className}.{methodName}");
                            }
                            continue;
                        }
                        else
                        {
                            // create handler
                            var handler = new MethodHandler(classType, methodInfo, methodName, new MethodHandlerOptions(options), serviceProvider);
                            if (!handlers.Add(handler))
                            {
                                throw new InvalidOperationException($"Method does not allow overload, {className}.{methodName}");
                            }
                        }
                    }

                    if (isStreamingHub)
                    {
                        var connectHandler = new MethodHandler(classType, classType.GetMethod("Connect") !, "Connect", new MethodHandlerOptions(options), serviceProvider);
                        if (!handlers.Add(connectHandler))
                        {
                            throw new InvalidOperationException($"Method does not allow overload, {className}.Connect");
                        }

                        streamingHubHandlers.AddRange(tempStreamingHubHandlers !);
                        StreamingHubHandlerRepository.RegisterHandler(connectHandler, tempStreamingHubHandlers.ToArray());
                        IGroupRepositoryFactory factory;
                        var attr = classType.GetCustomAttribute <GroupConfigurationAttribute>(true);
                        if (attr != null)
                        {
                            factory = attr.Create();
                        }
                        else
                        {
                            factory = serviceProvider.GetRequiredService <IGroupRepositoryFactory>();
                        }
                        StreamingHubHandlerRepository.AddGroupRepository(connectHandler, factory.CreateRepository(options.SerializerOptions, logger));
                    }
                }
            }
            catch (AggregateException agex)
            {
                ExceptionDispatchInfo.Capture(agex.InnerExceptions[0]).Throw();
            }

            var result = new MagicOnionServiceDefinition(handlers.ToArray(), streamingHubHandlers.ToArray());

            sw.Stop();
            logger.EndBuildServiceDefinition(sw.Elapsed.TotalMilliseconds);

            return(result);
        }
Beispiel #12
0
        /// <summary>
        /// Search MagicOnion service from target assemblies. ex: new[]{ typeof(Startup).GetTypeInfo().Assembly }
        /// </summary>
        /// <param name="serviceProvider">The service provider is used to resolve dependencies</param>
        /// <param name="searchAssemblies">The assemblies to be search for services</param>
        /// <param name="options">The options for MagicOnion server</param>
        public static MagicOnionServiceDefinition BuildServerServiceDefinition(IServiceProvider serviceProvider, Assembly[] searchAssemblies, MagicOnionOptions options)
        {
            var types = searchAssemblies
                        .SelectMany(x =>
            {
                try
                {
                    return(x.GetTypes());
                }
                catch (ReflectionTypeLoadException ex)
                {
                    return(ex.Types.Where(t => t != null));
                }
            });

#pragma warning disable CS8620 // Argument of type cannot be used for parameter of type in due to differences in the nullability of reference types.
            return(BuildServerServiceDefinition(serviceProvider, types, options));

#pragma warning restore CS8620 // Argument of type cannot be used for parameter of type in due to differences in the nullability of reference types.
        }
Beispiel #13
0
        /// <summary>
        /// Search MagicOnion service from all assemblies.
        /// </summary>
        /// <param name="serviceProvider">The service provider is used to resolve dependencies</param>
        /// <param name="options">The options for MagicOnion server</param>
        public static MagicOnionServiceDefinition BuildServerServiceDefinition(IServiceProvider serviceProvider, MagicOnionOptions options)
        {
            // NOTE: Exclude well-known system assemblies from automatic discovery of services.
            var wellKnownIgnoreAssemblies = new[]
            {
                "netstandard",
                "System.*",
                "Microsoft.Win32.*",
                "Microsoft.Extensions.*",
                "Microsoft.AspNetCore",
                "Microsoft.AspNetCore.*",
                "Grpc.*",
                "MessagePack",
                "MessagePack.*",
                "MagicOnion.Server",
                "MagicOnion.Server.*",
                "MagicOnion.Client",
                "MagicOnion.Client.*", // MagicOnion.Client.DynamicClient (MagicOnionClient.Create<T>)
                "MagicOnion.Abstractions",
                "MagicOnion.Shared",
            };

            var assemblies = AppDomain.CurrentDomain.GetAssemblies()
                             .Where(x =>
            {
                return(!wellKnownIgnoreAssemblies.Any(y =>
                {
                    if (y.EndsWith('*'))
                    {
                        return x.GetName().Name !.StartsWith(y.Substring(0, y.Length - 1));
                    }
                    else
                    {
                        return x.GetName().Name == y;
                    }
                }));
            })
                             .ToArray();

            return(BuildServerServiceDefinition(serviceProvider, assemblies, options));
        }