C# I';我们得到了一个JSON对象和一个消息类型。如何反序列化JSON并将其路由到类型安全的消息处理程序类?

C# I';我们得到了一个JSON对象和一个消息类型。如何反序列化JSON并将其路由到类型安全的消息处理程序类?,c#,json,C#,Json,我正在发布一条如下所示的消息: public class QueueMessage { public Guid Id { get; set; } public string MessageType { get; set; } public string Body { get; set; } public DateTime Received { get; set; } } public interface IMessageHandler<TMessage&g

我正在发布一条如下所示的消息:

public class QueueMessage
{
    public Guid Id { get; set; }
    public string MessageType { get; set; }
    public string Body { get; set; }
    public DateTime Received { get; set; }
}
public interface IMessageHandler<TMessage>
{
    void HandleMessage(Message<TMessage> message);
}
正文是JSON。
MessageType
指示JSON应该反序列化到哪种类型。(例如,如果
MessageType
等于“MessageOne”,那么我会将其反序列化为type
MessageOne

我希望我的消息处理程序是通用的,如下所示:

public class QueueMessage
{
    public Guid Id { get; set; }
    public string MessageType { get; set; }
    public string Body { get; set; }
    public DateTime Received { get; set; }
}
public interface IMessageHandler<TMessage>
{
    void HandleMessage(Message<TMessage> message);
}
公共接口IMessageHandler
{
无效HandleMessage(消息消息);
}
目前有几种类型,并将不断增长

但是,由于类型是在运行时确定的,我不能调用泛型类型而不进行反射。我还想从IoC容器中解析所有
IMessageHandler
实现

如何设置它,以便从非泛型代码调用泛型消息处理程序?

以下是一种方法

消息类和处理程序 首先,这里有一个接口,用于表示现有
QueueMessage
类的处理程序:

public interface IMessageHandler
{
    void HandleMessage(QueueMessage message);
}
然后,为了有一个通用消息处理程序,我们需要一个通用消息类

public class Message<TContent>
{
    public Message(TContent content, Guid id, DateTime received)
    {
        Content = content;
        Id = id;
        Received = received;
    }

    public TContent Content { get; }
    public Guid Id { get; }
    public DateTime Received { get; }
}
接下来,我们有一个
IMessageHandler
的实现,它将充当特定于类型的通用消息处理程序的门面。它的工作只是接收消息并对其进行路由,因此我们将其称为
MessageRouter

public class MessageRouter : IMessageHandler
{
    private readonly IMessageHandlerFactory _messageHandlerFactory;

    public MessageRouter(IMessageHandlerFactory messageHandlerFactory)
    {
        _messageHandlerFactory = messageHandlerFactory;
    }

    public void HandleMessage(QueueMessage message)
    {
        var handler = _messageHandlerFactory.GetHandler(message.MessageType);
        handler.HandleMessage(message);
    }
}
该类依赖于另一个抽象,
IMessageHandlerFactory
。它表示创建并返回特定于消息类型字符串的
IMessageHandler
实现的内容:

public interface IMessageHandlerFactory
{
    IMessageHandler GetHandler(string messageType);
}
我们如何实现
IMessageHandlerFactory
,以便它从IoC容器解析消息处理程序,而不直接引用容器?(这就像一个服务定位器,它还将工厂耦合到一个特定的IoC容器。)以下是一个实现:

public class MessageHandlerFactory : IMessageHandlerFactory
{
    private readonly Dictionary<string, Func<IMessageHandler>> _messageHandlers
        = new Dictionary<string, Func<IMessageHandler>>(StringComparer.OrdinalIgnoreCase);

    public void RegisterHandler(string messageType, Func<IMessageHandler> getHandlerFunction)
    {
        _messageHandlers[messageType] = getHandlerFunction;
    }

    public IMessageHandler GetHandler(string messageType)
    {
        if (_messageHandlers.ContainsKey(messageType))
            return _messageHandlers[messageType]();
        throw new InvalidOperationException($"No handler is registered for message type {messageType}.");
    }
}
然后我们有两个类-
MessageOneHandler
MessageTwoHandler
,它们实现了
IMessageHandler
IMessageHandler

public class MessageRouter : IMessageHandler
{
    private readonly IMessageHandlerFactory _messageHandlerFactory;

    public MessageRouter(IMessageHandlerFactory messageHandlerFactory)
    {
        _messageHandlerFactory = messageHandlerFactory;
    }

    public void HandleMessage(QueueMessage message)
    {
        var handler = _messageHandlerFactory.GetHandler(message.MessageType);
        handler.HandleMessage(message);
    }
}
向IServiceProvider注册所有这些内容。 如果我们使用的是
Microsoft.Extensions.DependencyInjection
-即
IServiceCollection
IServiceProvider
-我们如何注册所有这些?这里是一个扩展:

public static class MessageHandlerServiceCollectionExtensions
{
    public static IServiceCollection AddMessageHandlers(this IServiceCollection services)
    {
        services.RegisterHandler<MessageOne, MessageOneHandler>();
        services.RegisterHandler<MessageTwo, MessageTwoHandler>();

        // some string constants for message types would be better.
        services.AddSingleton<IMessageHandlerFactory>(serviceProvider =>
        {
            var factory = new MessageHandlerFactory();
            factory.RegisterHandler("MessageOne",
                serviceProvider.GetService<MessageHandlerWrapper<MessageOne>>);
            factory.RegisterHandler("MessageTwo",
                serviceProvider.GetService<MessageHandlerWrapper<MessageTwo>>);
            return factory;
        });

        services.AddSingleton<IMessageHandler, MessageRouter>();
        return services;
    }

    static void RegisterHandler<TMessageType, THandler>(this IServiceCollection services)
        where TMessageType : class
        where THandler : IMessageHandler<TMessageType>
    {
        services.AddSingleton<TMessageType>();
        services.AddSingleton(
            serviceProvider => 
                new MessageHandlerWrapper<TMessageType>(serviceProvider.GetService<THandler>())
        );
    }
}
在这个例子中,我使用了“魔术字符串”。在实际应用中,这些字符串应该是常量

这就是我们隐藏对
IServiceProvider
的调用的地方,这样就不会对它产生硬依赖。此外,在每种情况下,当函数返回
MessageHandlerWrapper
的泛型实现时,它被转换为
IMessageHandler
。即使该类是泛型的,使用者也会与非泛型的类进行交互介面

现在我们可以注入
IMessageHandlerFactory
,因为我们已经注册了一个实现

最后,我们将注册
IMessageHandler
的唯一实现,该实现可直接解析并注入任何位置:

services.AddSingleton<IMessageHandler, MessageRouter>();

集成测试可以解析
IMessageHandler
MessageRouter
)并运行包含JSON端到端的消息,确认消息得到路由和处理。

我自己回答这个问题,因为我在网站上和现实生活中都多次遇到过这个问题。这与我对另一个问题的回答几乎相同,但该问题的结构与answe建立了联系r不清楚。我宁愿创建另一个问题和自我回答,也不愿编辑其他人的问题以使答案有用。
services.AddSingleton<IMessageHandler, MessageRouter>();
[TestClass]
public class ServiceRegistrationTests
{
    [DataTestMethod]
    [DataRow("MessageOne")]
    [DataRow("MessageTwo")]
    public void FactoryResolvesMessageHandlers(string messageType)
    {
        var provider = GetServiceProvider();
        var factory = provider.GetService<IMessageHandlerFactory>();
        var handler = factory.GetHandler(messageType);
        Assert.IsNotNull(handler);
    }

    private IServiceProvider GetServiceProvider()
    {
        var services = new ServiceCollection();
        services.AddMessageHandlers();
        return services.BuildServiceProvider();
    }
}