c# - 如何在 Asp.net Core 运行时从 DependencyInjection 解析泛型类型服务

标签 c# generics asp.net-core dependency-injection type-inference

我正在处理一个将 XML 消息发送到我的服务器的微信项目。该消息可以是多种类型中的任何一种。因此,我首先将消息反序列化为具有基“WxMessage”的相应对象,然后将对象返回给调度程序,调度程序将找出正确的消息处理程序来处理消息。对应于每种消息类型的处理程序通过 IWxMessageHandler<> 接口(interface)注册到 Asp.net core 2.1 的 DependencyInjection。

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 的基础对象:

        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) 这样的显式调用方法时,结果是正确的。目前我正在通过旋转所有服务的列表然后选择我想要的一个来解决这个问题:

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

但是我想知道是否有更好的解决方案来解决这个问题。 如果有人可以提供帮助,谢谢。

最佳答案

问题在于泛型类型是在编译时求值的。所以即使你的 HandleMessage是一个通用的 HandleMessage<TMessage>(TMessage message) , TMessage 的类型使用的不是运行时类型。

你调用方法的方式是这样的:

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

因此 deserializedModel 的编译时类型确定将用于泛型类型参数的类型 TMessage .

Parse本身不是通用的,它可能会返回一个基类型,然后该基类型将用于 HandleMessage .因此,里面 HandleMessage , TMessage将是该基本类型和对 GetService<IWxMessageHandler<TMessage>> 的调用将使用该基类型来检索处理程序服务实例。

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

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

    var handler = _serviceProvider.GetService(messageHandlerType);

    // …
}

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

关于c# - 如何在 Asp.net Core 运行时从 DependencyInjection 解析泛型类型服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53249691/

相关文章:

c# - 无法解析目标框架 '.NETFramework,Version=v4.6.1' 的 mscorlib

c# - 使用 NoSQL 数据库进行 ASP.NET Core 身份验证

C# 将比较类作为泛型类型传递

c# - 如何在 ASP.NET Core 中添加全局 `AuthorizeFilter` 或 `AuthorizeAttribute`?

sql-server - .NET Core 对 SQL Server FILESTREAM 的支持

c# - 在 ASP.NET Core Web API 中注册一个新的 DelegatingHandler

c# - progressBar独立线程

c# - 在新的 AppDomain 中调用库以防止异常传播

java - java中的通用列表初始化

Java:用于保存不同类型基元的通用集合