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”,那么我会将其反序列化为typeMessageOne
)
我希望我的消息处理程序是通用的,如下所示:
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();
}
}