从客户端应用程序我发送请求/事务(包含执行(和参数)+ transactionID的操作)到远程队列。远程服务器在某个时候将请求出列并花费一些时间来处理它。
完成处理后,它会在客户端队列上发送一个响应(包含应用响应+ transactionID)...所以这是一个完全“断开连接”的通信模式,客户端可以映射的唯一方法对请求的响应是通过transactionID。
消息响应在客户端出列,并与原始请求匹配(基于transactionID)。
我现在正在做的是当客户端将请求发布到服务器队列时,它会向保留transactionId和callback(委托)的字典添加回调。这是一个Dictionary<int, object>
映射一个transactionId回到一个回调函数,调用该操作的结果。
回调/委托存储为对象,因为根据请求,回调委托签名是不同的(例如,响应可能返回List<string>
,而另一个响应可能返回int
})。
当客户端队列使响应出列时,它知道响应的类型(因此知道回调的相应签名),因此它根据transactionID从字典中获取回调。然后它将对象强制转换回相应的委托类型并调用回调。
我发现这种方法不是很“性感”,但我并没有真正看到另一种执行此类任务的方法。
有没有更好的方法来执行此操作?
如果问题不够明确,请告诉我并通过一些修改澄清。
答案 0 :(得分:3)
您可能会发现Reactive Extensions是一种用于此类消息传递的便捷工具。
首先,让所有响应实现一些接口或基类,如IMessage。每种响应都应该封装在一个单独的类中,比如这里
public interface IMessage
{
int TransactionId { get; }
}
public class UserListMessage : IMessage
{
int TransactionId { get; set; }
public List<string> Users { get; set; }
}
然后使您的消息队列实现IObservable<IMessage>
。 Reactive扩展提供了一个名为Subject<T>
的即用型实现,您可能希望将其包装起来。
Observable实现了一个Subscribe(IObserver<IMessage> observer)
方法,该方法将观察者存储在内部列表中的某个位置。当新响应到达时,将在每个订阅的观察者上调用OnNext(IMessage message)
方法。
最后,您的回复处理注册码可能如下所示:
var subscription = myQueue
.OfType<UserListMessage>()
.Where(msg => msg.TransactionId == id)
.Subscribe(callback);
这将为具有给定事务ID的UserListMessage类型的消息注册回调。 可能你也想在某个地方取消订阅。那将是:
subscription.Dispose();
这是一个简短的示例,看看Rx会是什么样子。现在去找一些更详细的教程。
答案 1 :(得分:1)
您可以在客户端创建一个枚举,用于定义您可以获得的所有可能响应类型。然后,您可以编写一个包含大“选择案例”的函数,该函数将枚举的每个值与其特定的函数调用相关联。 然后,您可以使用字典将transactionID与枚举值相关联,以识别您将从服务器获得的响应类型。
根据您需要在每种类型的响应上实现的功能类型,也许您可以找到更“面向对象”的做事方式...... 也许你可以使用一个常见的Respond方法创建一个基类ResponseAction,然后你可以从这个类继承你可以获得的每个可能的响应类型,然后当你调用服务器时,你实例化正确的Response_Specific_Action类,并且把这个实例放在字典中...然后在每个响应中你将使用你的字典找到正确的实例,并调用相同的标准Respond方法,它将使用它的Response_Specific_Action“特定”实现。
如果您使用ResponseAction类路由,您还可以考虑将transactionID包含为基类的属性。
答案 2 :(得分:1)
你的情况对我来说不太清楚。首先,您没有提到在客户端服务器事务期间使用哪种通信机制?你使用WCF及其'回调频道吗?
为什么不在基类下包装所有请求响应消息,该基类具有所有事务的一些公共数据?
你难以为列表使用List,因为响应因事务而异,但为什么还要反对呢?所有回复信息的共同合同难道不是更难吗?请告诉我们更多关于客户端和服务器之间的通信流程答案 3 :(得分:1)
这听起来就像是适配器模式问题。有关适配器的详细概述,请参阅this link。
您需要做的是创建一个抽象来封装整个客户端响应(包括您对List<string>
或int
所做的任何事情)。您的抽象需要在范围内足够宽,以便您的不同行为都可以由相同的签名覆盖。像这样:
public abstract class MessageCompleteHandler
{
public abstract void Execute();
}
//name this one better, just an example :)
public class ListOfStringHandler : MessageCompleteHandler
{
public override void Execute()
{
//get list of strings
// do something with it
}
}
public class MessageCompleteHandlerFactory
{
public MessageCompleteHandler GetHandler(int transactionId)
{
//this replaces/uses your dictionary to match handlers with types.
}
}
然后,当您收到响应时,使用事务ID创建适当的处理程序对象,然后运行它。如果你的处理程序变体数量相当小,那么这种方法效果最好。