public ListenableMethod(MethodInfo method, ListenerAttribute listener, Type requestType, Type responseType, Type parameterType, bool usesEnvelope) { Method = method; Listener = listener; RequestType = requestType; ResponseType = responseType; ParameterType = parameterType; UsesEnvelope = usesEnvelope; }
// TODO: Change this method to return something like Option<T> with a Success flag and an error message. private ListenableMethod GetListenableMethod(MethodInfo method, ListenerAttribute listener) { Type requestType = listener.Request.UnwrapEnvelopeType(); Type responseType = listener.Response.UnwrapEnvelopeType(); if (method.IsGenericMethodDefinition) { var numTypeArgs = method.GetGenericArguments().Length; if (numTypeArgs > 2) { _logger.Error($"Cannot subscribe method {method.Name} because method is generic and there is not enough information to construct it"); return(null); } if (numTypeArgs == 2 && requestType != null && responseType != null) { method = method.MakeGenericMethod(requestType, responseType); } if (numTypeArgs == 1 && (requestType ?? responseType) != null) { var typeToUse = requestType ?? responseType; _logger.Warn($"Method {method.Name} has one type argument. Assuming to use {typeToUse.Name}"); method = method.MakeGenericMethod(requestType); } } Type parameterType = method.GetParameters().FirstOrDefault()?.ParameterType; if (responseType != null && !responseType.IsAssignableFrom(method.ReturnType)) { _logger.Error($"Conflicting response types. {responseType.Name} not assignable from {method.ReturnType.Name}"); return(null); } // If the request type implements IRequest<TResponse> we can get the response type from there if (responseType == null) { var definedResponseType = requestType.GetRequestResponseType(); if (definedResponseType != null) { responseType = definedResponseType; } else { responseType = method.ReturnType; } } // Response MyMethod() // There is no parameter, so see if we have enough information to construct a trampoline if (parameterType == null) { // We have no type information at all, so we can't do anything if (requestType == null) { _logger.Error($"Could not determine which channel to use because payload type is not provided and there is no parameter"); return(null); } // This is not a fully-constructed type, so we can't determine a channel for it if (requestType.IsGenericType && !requestType.IsConstructedGenericType) { _logger.Error($"Payload type {requestType.Name} is not fully-constructed and cannot be used to define a channel"); return(null); } // Use the payload type from the attribute to create a trampoline to a method with no parameters return(new ListenableMethod(method, listener, requestType, responseType, null, false)); } // parameterType is the type of the raw parameter, which may include Envelope<> // parameterPayloadType is the type of the payload without Envelope<> bool isEnvelope = parameterType.IsEnvelopeType(); Type parameterPayloadType = parameterType.UnwrapEnvelopeType(); if (requestType == null) { requestType = parameterPayloadType; } // void MyMethod<T>(T payload) or void MyMethod<T>(Envelope<T> payload) // This is not a fully-constructed type, so we can't determine a channel for it if (requestType.IsGenericType && !requestType.IsConstructedGenericType) { _logger.Error($"Payload type {requestType.Name} is not fully-constructed and cannot be used"); return(null); } // Check that the type information between parameter and specified payload type are assignable if (!parameterPayloadType.IsAssignableFrom(requestType)) { _logger.Error($"Cannot subscribe method {method.Name} because specified payload type {requestType.Name} is not assignable to parameter type {parameterPayloadType.Name}"); return(null); } return(new ListenableMethod(method, listener, requestType, responseType, parameterType, isEnvelope)); }