public MethodDescriptor(HubDescriptor hub, MethodInfo methodInfo) { var classType = hub.HubType; var opAttr = methodInfo.GetCustomAttributes<OperationAttribute>().FirstOrDefault(); if (opAttr == null) throw new InvalidOperationException($"Method needs OperationAttribute, class:{classType.Name} method:{methodInfo.Name}"); this.Hub = hub; this.OperationCode = opAttr.OperationCode; this.MethodName = methodInfo.Name; this.Arguments = methodInfo.GetParameters() .Select(x => new ParameterInfoSlim(x)) .ToArray(); this.ParameterNames = Arguments.Select(x => x.Name).ToList().AsReadOnly(); this.ReturnType = methodInfo.ReturnType; this.filters = classType.GetCustomAttributes<PhotonWireFilterAttribute>(true) .Concat(methodInfo.GetCustomAttributes<PhotonWireFilterAttribute>(true)) .OrderBy(x => x.Order) .ToArray(); this.AttributeLookup = classType.GetCustomAttributes(true) .Concat(methodInfo.GetCustomAttributes(true)) .Cast<Attribute>() .ToLookup(x => x.GetType()); // prepare lambda parameters var contextArg = Expression.Parameter(typeof(OperationContext), "context"); var contextBind = Expression.Bind(classType.GetProperty("Context"), contextArg); 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(this.ReturnType)) { // (object[] args) => new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda<Func<OperationContext, object[], Task>>( Expression.Call( Expression.MemberInit(Expression.New(classType), contextBind), methodInfo, parameters), contextArg, args); if (this.ReturnType.IsGenericType && this.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) { this.handlerBodyType = HandlerBodyType.AsyncFunc; this.methodAsyncFuncBody = lambda.Compile(); lock (taskResultExtractors) { if (!taskResultExtractors.ContainsKey(this.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, this.ReturnType), "Result"), typeof(object)), taskParameter); var compiledResultLambda = resultLambda.Compile(); taskResultExtractors[this.ReturnType] = compiledResultLambda; } } } else { this.handlerBodyType = HandlerBodyType.AsyncAction; this.methodAsyncActionBody = lambda.Compile(); } } else if (this.ReturnType == typeof(void)) // of course void { // (object[] args) => { new X().M((T1)args[0], (T2)args[1])... } var lambda = Expression.Lambda<Action<OperationContext, object[]>>( Expression.Call( Expression.MemberInit(Expression.New(classType), contextBind), methodInfo, parameters), contextArg, args); this.handlerBodyType = HandlerBodyType.Action; this.methodActionBody = lambda.Compile(); } else // return T { // (object[] args) => (object)new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda<Func<OperationContext, object[], object>>( Expression.Convert( Expression.Call( Expression.MemberInit(Expression.New(classType), contextBind), methodInfo, parameters) , typeof(object)), contextArg, args); this.handlerBodyType = HandlerBodyType.Func; this.methodFuncBody = lambda.Compile(); } }
public MethodDescriptor(HubDescriptor hub, MethodInfo methodInfo) { var classType = hub.HubType; var opAttr = methodInfo.GetCustomAttributes <OperationAttribute>().FirstOrDefault(); if (opAttr == null) { throw new InvalidOperationException($"Method needs OperationAttribute, class:{classType.Name} method:{methodInfo.Name}"); } this.Hub = hub; this.OperationCode = opAttr.OperationCode; this.MethodName = methodInfo.Name; this.Arguments = methodInfo.GetParameters() .Select(x => new ParameterInfoSlim(x)) .ToArray(); this.ParameterNames = Arguments.Select(x => x.Name).ToList().AsReadOnly(); this.ReturnType = methodInfo.ReturnType; this.filters = classType.GetCustomAttributes <PhotonWireFilterAttribute>(true) .Concat(methodInfo.GetCustomAttributes <PhotonWireFilterAttribute>(true)) .OrderBy(x => x.Order) .ToArray(); this.AttributeLookup = classType.GetCustomAttributes(true) .Concat(methodInfo.GetCustomAttributes(true)) .Cast <Attribute>() .ToLookup(x => x.GetType()); // prepare lambda parameters var contextArg = Expression.Parameter(typeof(OperationContext), "context"); var contextBind = Expression.Bind(classType.GetProperty("Context"), contextArg); 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(this.ReturnType)) { // (object[] args) => new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda <Func <OperationContext, object[], Task> >( Expression.Call( Expression.MemberInit(Expression.New(classType), contextBind), methodInfo, parameters), contextArg, args); if (this.ReturnType.IsGenericType && this.ReturnType.GetGenericTypeDefinition() == typeof(Task <>)) { this.handlerBodyType = HandlerBodyType.AsyncFunc; this.methodAsyncFuncBody = lambda.Compile(); lock (taskResultExtractors) { if (!taskResultExtractors.ContainsKey(this.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, this.ReturnType), "Result"), typeof(object)), taskParameter); var compiledResultLambda = resultLambda.Compile(); taskResultExtractors[this.ReturnType] = compiledResultLambda; } } } else { this.handlerBodyType = HandlerBodyType.AsyncAction; this.methodAsyncActionBody = lambda.Compile(); } } else if (this.ReturnType == typeof(void)) // of course void { // (object[] args) => { new X().M((T1)args[0], (T2)args[1])... } var lambda = Expression.Lambda <Action <OperationContext, object[]> >( Expression.Call( Expression.MemberInit(Expression.New(classType), contextBind), methodInfo, parameters), contextArg, args); this.handlerBodyType = HandlerBodyType.Action; this.methodActionBody = lambda.Compile(); } else // return T { // (object[] args) => (object)new X().M((T1)args[0], (T2)args[1])... var lambda = Expression.Lambda <Func <OperationContext, object[], object> >( Expression.Convert( Expression.Call( Expression.MemberInit(Expression.New(classType), contextBind), methodInfo, parameters) , typeof(object)), contextArg, args); this.handlerBodyType = HandlerBodyType.Func; this.methodFuncBody = lambda.Compile(); } }
internal static HubDescriptor CreateIfPossible(Type type) { HubKind kind; if (typeof(IHub).IsAssignableFrom(type)) { kind = HubKind.Client; } else if (typeof(ServerHub).IsAssignableFrom(type)) { kind = HubKind.Server; } else if (typeof(ReceiveServerHub).IsAssignableFrom(type)) { kind = HubKind.ReceiveServer; } else { return null; } if (type.IsAbstract) return null; if (type.GetCustomAttribute<IgnoreOperationAttribute>(true) != null) return null; // ignore var className = type.Name; if (!type.GetConstructors().Any(x => x.GetParameters().Length == 0)) { throw new InvalidOperationException(string.Format("Hub needs parameterless constructor, class:{0}", type.FullName)); } var hubAttr = type.GetCustomAttributes<HubAttribute>(false).FirstOrDefault(); if (hubAttr == null) { throw new InvalidOperationException(string.Format("Hub needs HubAttribute, class:{0}", type.FullName)); } var clientType = (kind == HubKind.Client) ? FindHubClientType(type) : typeof(INoClient); var tags = new HashSet<string>(type.GetCustomAttributes<HubTag>(true).SelectMany(x => x.Tags)); var hub = new HubDescriptor() { HubName = className, HubType = type, ClientType = clientType, HubKind = kind, HubId = hubAttr.HubId, CanExecute = true, HubTags = new ReadOnlyHashSet<string>(tags) }; foreach (var methodInfo in type.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 MethodDescriptor(hub, methodInfo); if (hub.Methods.ContainsKey(handler.OperationCode)) { throw new InvalidOperationException(string.Format("same operationCode is not allowed, class:{0} method:{1} opCode:{2}", className, methodName, handler.OperationCode)); } else { hub.Methods.Add(handler.OperationCode, handler); } } return hub; }
internal OperationContext(HubDescriptor hub, MethodDescriptor method, IPhotonWirePeer peer, OperationRequest operationRequest, SendParameters sendParameters, DateTime timestamp) : this(hub.HubId, peer, operationRequest, sendParameters, timestamp) { this.Hub = hub; this.Method = method; }
internal HubContext(HubDescriptor hub, SendParameters sendParameters) { this.Hub = hub; this.SendParameters = sendParameters; }
internal HubContext(HubDescriptor hub) { this.Hub = hub; this.SendParameters = default(SendParameters); }