如何在Asp.net Core运行时从DependencyInjection解析通用类型服务 [英] How to resolve a generic type service from the DependencyInjection at Asp.net Core runtime

查看:120
本文介绍了如何在Asp.net Core运行时从DependencyInjection解析通用类型服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在处理一个将XML消息发布到我的服务器的微信项目。该消息可以是几种类型中的任何一种。因此,我首先使用基本的 WxMessage将消息反序列化为相应的对象,然后将该对象返回给调度程序,该调度程序将找到正确的消息处理程序来处理该消息。对应于每种消息类型的处理程序通过IWxMessageHandler接口注册到Asp.net核心2.1的DependencyInjection。

I am dealing with a WeChat project that post XML message to my server. The message could be any one of several types. Hence I first deserialize the message to corresponding object with a base "WxMessage", and then return the object to a dispatcher, which will find out a correct message handler to handle the message. The handlers, corresponding to each type of messages, were registered to the DependencyInjection of Asp.net core 2.1 with a IWxMessageHandler<> interface.

services.AddScoped<IWxMessageHandler<WxMessage_Image>, WxMessageHandler_Image>();
services.AddScoped<IWxMessageHandler<WxMessage_Text>, WxMessageHandler_Text>();
services.AddScoped<IWxMessageHandler<WxMessage_File>, WxMessageHandler_File>();

这是消息服务:

    public async Task<string> Handle(string messageBody)
    {
        var reply = default(WxMessage);

        using (var deserial = new WxInMessageDeserializer())
        {
            var deserializedModel =  deserial.Parse(messageBody);

            reply = (WxReplyMessage_Text) await HandleMessage(deserializedModel);
        }
        return await Task.FromResult(BuildXmlContent(reply));
    }

    public async Task<WxMessage> HandleMessage<TMessage>(TMessage message) 
    {
        var _handler = _serviceProvider.GetService<IWxMessageHandler<TMessage>>();
        ......
    }

但是它未能解决IWxMessageHandler的正确实现,因为WxInMessageDeserializer返回WxMessage的基础对象:

However it failed to resolve the correct implement of IWxMessageHandler<> as the WxInMessageDeserializer return a base object of WxMessage:

        var result = default(WxMessage);
        switch (_msgTYpe)
            {
                case "text": result = (WxMessage_Text)new XmlSerializer(typeof(WxMessage_Text))
                        .Deserialize(new StringReader(rawMessage));
                    break;
                case "image": result = (WxMessage_Image)new XmlSerializer(typeof(WxMessage_Image))
                        .Deserialize(new StringReader(rawMessage));
                    break;
                case "voice": result = (WxPushMessage_Voice)new XmlSerializer(typeof(WxPushMessage_Voice))
                        .Deserialize(new StringReader(rawMessage));
                    break;
                ......
                case "file": result = (WxMessage_File)new XmlSerializer(typeof(WxMessage_File))
                        .Deserialize(new StringReader(rawMessage));
                    break;
            }
        return result;

我想这是C#类型推断的局限性,因为当我使用显式时,结果是正确的像HandleMessage(somemessage)这样的调用方法。目前,我正在通过旋转所有服务的列表来解决此问题,然后选择我想要的服务:

I guess this is a limitation of the C# Type Inferencing, as the result was correct when I use explicit calling method like HandleMessage(somemessage). Currently I am solving this issue by revolving a list of all services and then select the one I want:

        var type = typeof(TMessage);
        var _handler = _handlers
            .Where(h => h.GetHandlingType().Equals(message.GetType()))
            .FirstOrDefault();
        var result = _handler.Handle(message);
        return await result;

但是我想知道是否有更好的解决方案来解决这个问题。
感谢任何人的帮助。

Yet I am wondering if there is any better solutions to this issue. Thanks if anyone can help.

推荐答案

问题是泛型类型是在编译时求值的。因此,即使您的 HandleMessage 是通用的 HandleMessage< TMessage>(TMessage消息),类型 TMessage 不是运行时类型。

The problem is that generic types are evaluated at compile time. So even if your HandleMessage is a generic HandleMessage<TMessage>(TMessage message), the type of TMessage that is used is not a runtime type.

调用方法的方式如下:

var deserializedModel = deserial.Parse(messageBody);
reply = (WxReplyMessage_Text) await HandleMessage(deserializedModel);

所以 deserializedModel 的编译时类型确定通用类型参数 TMessage 将使用哪种类型。

So the compile-time type of deserializedModel determines what type will be used for the generic type argument TMessage.

由于 Parse 本身不是通用的,它可能会返回一个基本类型,然后该基本类型将用于 HandleMessage 。这样,在 HandleMessage 中, TMessage 将是该基本类型,并调用 GetService<。 IWxMessageHandler< TMessage>> 将使用该基本类型来检索处理程序服务实例。

Since Parse is not generic itself, it will likely return a base type, and that base type will then be used for HandleMessage. As such, inside HandleMessage, TMessage will be that base type and the call to GetService<IWxMessageHandler<TMessage>> will use that base type to retrieve the handler service instance.

如果要获取实际的处理程序,则可以将必须从服务提供商检索具体服务类型。为了从您的消息对象的运行时类型做到这一点,您将需要使用反射来检索和构造该类型:

If you want to get the actual handler, you will have to retrieve the concrete service type from the service provider. And in order to do that from a run-time type of your message object, you will need to use reflection to retrieve and construct that type:

public async Task<WxMessage> HandleMessage(IWxMessage message) 
{
    var messageType = message.GetType();
    var messageHandlerType = typeof(IWxMessageHandler<>).MakeGenericType(messageType);

    var handler = _serviceProvider.GetService(messageHandlerType);

    // …
}

这假设您有基本类型为 IWxMessage 的消息,并使 HandleMessage 方法非通用。相反,处理程序类型将在编译时从具体消息类型解析。请注意,您还应该为您的消息处理程序提供一些基本接口,该接口允许您在所有类型上调用方法(理想情况下只接受 IWxMessage ),否则您还必须使用反射调用处理程序。

This assumes that you have a base type IWxMessage for the message and makes the HandleMessage method non-generic. Instead, the handler type will be resolved at compile-time from the concrete message type. Note that you should also have some base interface for your message handler that allows you to call a method on all types (ideally just taking an IWxMessage), otherwise you will also have to use reflection to call the handler.

这篇关于如何在Asp.net Core运行时从DependencyInjection解析通用类型服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆