If I send a HTTP Get request:
/api/Company/1
I have an OwinMiddleware
where I'm using the context
to ascertain path
and json content of an IAsyncRequest<T>
.
To know which async request to use I have a mapping of path to Type
of IAsyncRequest<T>
var mappings = new Dictionary<string, Type> { ["api/Company/{id}"] = typeof(GetCompanyRequest) }
Type request;
var result = mappings.TryGetValue(context.Requst.Path.Value, out request);
I use the JObject
to create an instance of a GetCompanyRequest
var get = new JObject { ["id"] = "1" /* obtained from the url */ }
var instantiatedRequest = JObject.ToObject(request);
The reason I use JObject
is that for PUT and POST requests I deserialise the JSON body straight into a request.
The last piece of the puzzle is now sending off this object instantiatedRequest
through the mediator pipeline. Obviously Task<T> SendAsync<T>(IAsyncRequest<T> request)
isn't going to work.
The interesting thing is, I don't need to know T
because I will always be serialising it into a string
to post back to the user.
So can the signature Task<object> SendAsync(object request)
be worked into the current mediator framework to accomodate this? (Not asking for it to be done, just is it possible?)
I found this in the mediator.cs
private TWrapper GetHandler<TWrapper, TResponse>(object request, Type handlerType, Type wrapperType)
{
var requestType = request.GetType();
var genericHandlerType = _genericHandlerCache.GetOrAdd(requestType, handlerType, (type, root) => root.MakeGenericType(type, typeof(TResponse)));
var genericWrapperType = _wrapperHandlerCache.GetOrAdd(requestType, wrapperType, (type, root) => root.MakeGenericType(type, typeof(TResponse)));
var handler = GetHandler(request, genericHandlerType);
return (TWrapper) Activator.CreateInstance(genericWrapperType, handler);
}
private object GetHandler(object request, Type handlerType)
{
try
{
return _singleInstanceFactory(handlerType);
}
catch (Exception e)
{
throw BuildException(request, e);
}
}
That second GetHandler
has the parameters I need, the first one is what is called into by SendAsync
, I don't see an issue with sticking something in.
Any concerns with doing it?
So there is a way without modifying the source code to achieve this:
var irequestInterface = typeof(GetCompanyRequest).GetInterfaces().FirstOrDefault(x => x.Name.StartsWith("IRequest"));
if (irequestInterface == null)
{
throw new Exception("IRequest is null");
}
var tresponse = irequestInterface.GenericTypeArguments.FirstOrDefault();
if (tresponse == null)
{
throw new Exception("Reponse is null");
}
var method = typeof(IMediator).GetMethod("Send");
var generic = method.MakeGenericMethod(tresponse);
generic.Invoke(mediator, new []{ (object)Activator.CreateInstance<GetCompanyRequest>() });
The last bit is from Jon Skeets answer
The first bit is because we don't care about TResponse
so I don't want to have to specify it. Our GetCompanyRequest
has more than enough information on it to execute SendAsync<TResponse>
.
Normally I would be weary of using reflection, but I know that the implementation of Mediator
uses a lot of reflection - so you know what - we'll just bundle all these issues into one.