簡體   English   中英

如何在Asp.net Core運行時從DependencyInjection解析通用類型服務

[英]How to resolve a generic type service from the DependencyInjection at Asp.net Core runtime

我正在處理將XML消息發布到服務器的微信項目。 該消息可以是幾種類型中的任何一種。 因此,我首先使用基本的“ WxMessage”將消息反序列化為相應的對象,然后將該對象返回給分派器,后者將找出正確的消息處理程序來處理該消息。 與每種消息類型相對應的處理程序都使用IWxMessageHandler <>接口注冊到Asp.net核心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>>();
        ......
    }

但是,由於WxInMessageDeserializer返回WxMessage的基礎對象,因此無法解析IWxMessageHandler <>的正確實現:

        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方法為非泛型的。 取而代之的是,處理程序類型將在編譯時從具體消息類型中解析出來。 請注意,您還應該為消息處理程序提供一些基本接口,該接口允許您在所有類型上調用方法(理想情況下只是采用IWxMessage ),否則,您還必須使用反射來調用處理程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM