public MethodHandlerOptions(MagicOnionOptions options) { GlobalFilters = options.GlobalFilters; IsReturnExceptionStackTraceInErrorDetail = options.IsReturnExceptionStackTraceInErrorDetail; EnableCurrentContext = options.EnableCurrentContext; SerializerOptions = options.SerializerOptions; }
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); }
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()}"); } }
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)); }
/// <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); }
/// <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. }
/// <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)); }