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 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); }