MediatR 无需键入即可发送对象
MediatR Send object without typing
如果我发送 HTTP Get 请求:
/api/Company/1
我有一个 OwinMiddleware
,我正在使用 context
来确定 IAsyncRequest<T>
的 path
和 json 内容。
要知道要使用哪个异步请求,我有一个到 Type
的 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);
我使用 JObject
创建一个 GetCompanyRequest
的实例
var get = new JObject { ["id"] = "1" /* obtained from the url */ }
var instantiatedRequest = JObject.ToObject(request);
我使用 JObject
的原因是,对于 PUT 和 POST 请求,我将 JSON 主体直接反序列化为请求。
拼图的最后一块现在正在通过中介管道发送此 object instantiatedRequest
。显然 Task<T> SendAsync<T>(IAsyncRequest<T> request)
是行不通的。
有趣的是,我不需要知道 T
因为我总是将它序列化为 string
到 post 返回给用户。
那么可以将签名 Task<object> SendAsync(object request)
加入当前的调解器框架中以适应这种情况吗? (不是要求完成,只是可能吗?)
查看源代码
我在 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);
}
}
第二个 GetHandler
有我需要的参数,第一个是 SendAsync
调用的参数,我认为插入一些东西没有问题。
这样做有什么顾虑吗?
所以有一种不修改源码的方法可以实现:
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>() });
最后一位来自Jon Skeets answer
第一位是因为我们不关心 TResponse
所以我不想指定它。我们的 GetCompanyRequest
有足够的信息来执行 SendAsync<TResponse>
.
通常我会厌倦使用反射,但我知道 Mediator
的实现使用了很多反射 - 所以你知道吗 - 我们只是将所有这些问题捆绑在一起。
这就是我从 RabbitMqRequestHandler 调用 MediatR 的方法。基本上,我想使用队列消息触发任何命令类型。这个概念证明帮助我创建了另一个功能。
重要提示:这适用于 (MediatR 7.0.0)
当我将它升级到版本 9 时,我的功能停止工作。
开始吧:
private async Task HandleMessage(SmartConfigQueueMessage message)
{
try
{
var type = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(x => x.GetTypes())
.FirstOrDefault(t => t.Name == message.OperationName);
var resultType = type.GetInterfaces()
.Where(r => r.FullName.Contains("SmartConfig"))? //MY ASSEMBLY MAIN NAMESPACE
.FirstOrDefault().GenericTypeArguments
.FirstOrDefault();
dynamic command = message.Variables.ToObject(type);
var method = _mediator.GetType().GetMethod("Send");
var generic = method.MakeGenericMethod(resultType);
var response = generic.InvokeAsync(_mediator, new object[] {command, new CancellationToken()});
}
catch (Exception ex)
{
_logger.LogInformation($"Consumer handler error: {ex.Message}");
}
// We just print this message
_logger.LogInformation($"Consumer message received: {message.OperationName}");
}
MediatR 异步处理所有查询和命令。所以,我们需要这个扩展:
public static class GenericMethodExtensions
{
public static async Task<object> InvokeAsync(this MethodInfo @this, object obj, params object[] parameters)
{
var task = (Task)@this.Invoke(obj, parameters);
await task.ConfigureAwait(false);
var resultProperty = task.GetType().GetProperty("Result");
return resultProperty?.GetValue(task);
}
}
如果我发送 HTTP Get 请求:
/api/Company/1
我有一个 OwinMiddleware
,我正在使用 context
来确定 IAsyncRequest<T>
的 path
和 json 内容。
要知道要使用哪个异步请求,我有一个到 Type
的 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);
我使用 JObject
创建一个 GetCompanyRequest
var get = new JObject { ["id"] = "1" /* obtained from the url */ }
var instantiatedRequest = JObject.ToObject(request);
我使用 JObject
的原因是,对于 PUT 和 POST 请求,我将 JSON 主体直接反序列化为请求。
拼图的最后一块现在正在通过中介管道发送此 object instantiatedRequest
。显然 Task<T> SendAsync<T>(IAsyncRequest<T> request)
是行不通的。
有趣的是,我不需要知道 T
因为我总是将它序列化为 string
到 post 返回给用户。
那么可以将签名 Task<object> SendAsync(object request)
加入当前的调解器框架中以适应这种情况吗? (不是要求完成,只是可能吗?)
查看源代码
我在 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);
}
}
第二个 GetHandler
有我需要的参数,第一个是 SendAsync
调用的参数,我认为插入一些东西没有问题。
这样做有什么顾虑吗?
所以有一种不修改源码的方法可以实现:
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>() });
最后一位来自Jon Skeets answer
第一位是因为我们不关心 TResponse
所以我不想指定它。我们的 GetCompanyRequest
有足够的信息来执行 SendAsync<TResponse>
.
通常我会厌倦使用反射,但我知道 Mediator
的实现使用了很多反射 - 所以你知道吗 - 我们只是将所有这些问题捆绑在一起。
这就是我从 RabbitMqRequestHandler 调用 MediatR 的方法。基本上,我想使用队列消息触发任何命令类型。这个概念证明帮助我创建了另一个功能。
重要提示:这适用于 (MediatR 7.0.0) 当我将它升级到版本 9 时,我的功能停止工作。
开始吧:
private async Task HandleMessage(SmartConfigQueueMessage message)
{
try
{
var type = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(x => x.GetTypes())
.FirstOrDefault(t => t.Name == message.OperationName);
var resultType = type.GetInterfaces()
.Where(r => r.FullName.Contains("SmartConfig"))? //MY ASSEMBLY MAIN NAMESPACE
.FirstOrDefault().GenericTypeArguments
.FirstOrDefault();
dynamic command = message.Variables.ToObject(type);
var method = _mediator.GetType().GetMethod("Send");
var generic = method.MakeGenericMethod(resultType);
var response = generic.InvokeAsync(_mediator, new object[] {command, new CancellationToken()});
}
catch (Exception ex)
{
_logger.LogInformation($"Consumer handler error: {ex.Message}");
}
// We just print this message
_logger.LogInformation($"Consumer message received: {message.OperationName}");
}
MediatR 异步处理所有查询和命令。所以,我们需要这个扩展:
public static class GenericMethodExtensions
{
public static async Task<object> InvokeAsync(this MethodInfo @this, object obj, params object[] parameters)
{
var task = (Task)@this.Invoke(obj, parameters);
await task.ConfigureAwait(false);
var resultProperty = task.GetType().GetProperty("Result");
return resultProperty?.GetValue(task);
}
}