// cache all methods public void RegisterHandler(Assembly[] hostAssemblies) { if (Interlocked.Increment(ref alreadyRegistered) != 0) return; var contractTypes = hostAssemblies .SelectMany(x => { try { return x.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where(t => t != null); } }) .Where(x => typeof(LightNodeContract).IsAssignableFrom(x)) .Where(x => !x.IsAbstract); Parallel.ForEach(contractTypes, 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)); } if (classType.GetCustomAttribute<IgnoreOperationAttribute>(true) != null) return; // ignore foreach (var methodInfo in classType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { if (methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_") || methodInfo.Name.StartsWith("get_"))) continue; // as property if (methodInfo.GetCustomAttribute<IgnoreOperationAttribute>(true) != null) continue; // ignore var methodName = methodInfo.Name; // ignore default methods if (methodName == "Equals" || methodName == "GetHashCode" || methodName == "GetType" || methodName == "ToString") { continue; } // create handler var handler = new OperationHandler(options, classType, methodInfo); lock (handlers) { // fail duplicate entry var path = new RequestPath(className, methodName); if (handlers.ContainsKey(path)) { throw new InvalidOperationException(string.Format("same class and method is not allowed, class:{0} method:{1}", className, methodName)); } handlers.Add(path, handler); } } }); }
internal OperationInfo(OperationHandler handler) { this.ClassName = handler.ClassName; this.MethodName = handler.MethodName; this.AcceptVerbs = handler.AcceptVerb; this.Parameters = handler.Arguments; this.ReturnType = handler.ReturnType; this.ForceUseFormatter = handler.ForceUseFormatter; }
// cache all methods public IReadOnlyCollection <KeyValuePair <string, OperationInfo> > RegisterHandler(Assembly[] hostAssemblies) { if (Interlocked.Increment(ref alreadyRegistered) != 0) { return(new KeyValuePair <string, OperationInfo> [0]); } var contractTypes = hostAssemblies .SelectMany(x => { try { return(x.GetTypes()); } catch (ReflectionTypeLoadException ex) { return(ex.Types.Where(t => t != null)); } }) .Where(x => typeof(LightNodeContract).IsAssignableFrom(x)) .Where(x => !x.IsAbstract); Parallel.ForEach(contractTypes, 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)); } if (classType.GetCustomAttribute <IgnoreOperationAttribute>(true) != null) { return; // ignore } foreach (var methodInfo in classType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { if (methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_") || methodInfo.Name.StartsWith("get_"))) { continue; // as property } if (methodInfo.GetCustomAttribute <IgnoreOperationAttribute>(true) != null) { continue; // ignore } var methodName = methodInfo.Name; // ignore default methods if (methodName == "Equals" || methodName == "GetHashCode" || methodName == "GetType" || methodName == "ToString") { continue; } var sw = Stopwatch.StartNew(); // create handler var handler = new OperationHandler(options, classType, methodInfo); lock (handlers) { // fail duplicate entry var path = new RequestPath(className, methodName); if (handlers.ContainsKey(path)) { throw new InvalidOperationException(string.Format("same class and method is not allowed, class:{0} method:{1}", className, methodName)); } handlers.Add(path, handler); } sw.Stop(); options.Logger.RegisiterOperation(handler.ClassName, handler.MethodName, sw.Elapsed.TotalMilliseconds); } }); // return readonly operation info return(handlers.Select(x => new KeyValuePair <string, OperationInfo>(x.Key.ToString(), new OperationInfo(x.Value))).ToList().AsReadOnly()); }
public static void RegisterHandler(Assembly[] hostAssemblies) { if (Interlocked.Increment(ref alreadyRegistered) != 0) return; var contractTypes = hostAssemblies .SelectMany(x => x.GetTypes()) .Where(x => typeof(LightNodeContract).IsAssignableFrom(x)); Parallel.ForEach(contractTypes, classType => { var className = classType.Name; foreach (var methodInfo in classType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { if (methodInfo.IsSpecialName && (methodInfo.Name.StartsWith("set_") || methodInfo.Name.StartsWith("get_"))) continue; // as property var methodName = methodInfo.Name; // ignore default methods if (methodName == "Equals" || methodName == "GetHashCode" || methodName == "GetType" || methodName == "ToString") { continue; } var handler = new OperationHandler(); handler.MethodName = methodName; handler.Arguments = methodInfo.GetParameters(); handler.ReturnType = methodInfo.ReturnType; foreach (var argument in handler.Arguments) { if (!AllowRequestType.IsAllowType(argument.ParameterType)) { throw new InvalidOperationException(string.Format("parameter is not allowed, class:{0} method:{1} paramName:{2} paramType:{3}", className, methodName, argument.Name, argument.ParameterType.FullName)); } } // prepare lambda parameters var envArg = Expression.Parameter(typeof(IDictionary<string, object>), "environment"); var envBind = Expression.Bind(typeof(LightNodeContract).GetProperty("Environment"), envArg); var args = Expression.Parameter(typeof(object[]), "args"); var parameters = methodInfo.GetParameters() .Select((x, i) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(i)), x.ParameterType)) .ToArray(); // Task or Task<T> if (typeof(Task).IsAssignableFrom(handler.ReturnType)) { // (object[] args) => new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda<Func<IDictionary<string, object>, object[], Task>>( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters), envArg, args); if (handler.ReturnType.IsGenericType && handler.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) { handler.HandlerBodyType = HandlerBodyType.AsyncFunc; handler.MethodAsyncFuncBody = lambda.Compile(); lock (taskResultExtractors) { if (!taskResultExtractors.ContainsKey(handler.ReturnType)) { // (object task) => (object)((Task<>).Result) var taskParameter = Expression.Parameter(typeof(object), "task"); var resultLambda = Expression.Lambda<Func<object, object>>( Expression.Convert( Expression.Property( Expression.Convert(taskParameter, handler.ReturnType), "Result"), typeof(object)), taskParameter); var compiledResultLambda = resultLambda.Compile(); taskResultExtractors[handler.ReturnType] = compiledResultLambda; } } } else { handler.HandlerBodyType = HandlerBodyType.AsyncAction; handler.MethodAsyncActionBody = lambda.Compile(); } } else if (handler.ReturnType == typeof(void)) // of course void { // (object[] args) => { new X().M((T1)args[0], (T2)args[1])... } var lambda = Expression.Lambda<Action<IDictionary<string, object>, object[]>>( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters), envArg, args); handler.HandlerBodyType = HandlerBodyType.Action; handler.MethodActionBody = lambda.Compile(); } else // return T { // (object[] args) => (object)new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda<Func<IDictionary<string, object>, object[], object>>( Expression.Convert( Expression.Call( Expression.MemberInit(Expression.New(classType), envBind), methodInfo, parameters) , typeof(object)), envArg, args); handler.HandlerBodyType = HandlerBodyType.Func; handler.MethodFuncBody = lambda.Compile(); } lock (handlers) { // fail duplicate entry var path = new RequestPath(className, methodName); if (handlers.ContainsKey(path)) { throw new InvalidOperationException(string.Format("same class and method is not allowed, class:{0} method:{1}", className, methodName)); } handlers.Add(path, handler); } } }); }