I am attempting to write a generic messaging passing system for Delphi and .NET. The system allows messages to be defined as plain objects and message handlers are defined as anonymous methods that act on those objects.
The objects are converted to JSON and passed between applications running on the same machine. Each application maintains a list of handlers that understand specific message types.
My class has a number of parameterized registration methods. Some take a single type parameter. Some take a pair of parameters where one represents a request object and the other a response object. Here is what the Request/Response handler registration looks like:
procedure TTelegraph.RegisterRequestHandler<TRequest, TResponse>
(requestTypeToHandle: string; handler: TFunc<TRequest, TResponse>);
begin
FRequestHandlers.Add(requestTypeToHandle,
TRequestHandler<TRequest, TResponse>.Create(handler,
TRequest,
TResponse));
end;
FRequestHandlers
is a TDictionary<string,TRequestHandler>
. The registration method is called like so:
FTelegraph.RegisterRequestHandler<TTestRequest, TTestResponse>('My Request',
function(x: TTestRequest): TTestResponse
begin
Result := TTestResponse.Create;
Result.Number := x.Number;
Result.Message := Format('Received: %s', [x.Message]);
end);
The generic TRequestHandler<T1,T2>
is a DTO that wraps the handler along with the types TRequest
and TResponse
. It inherits from the non-generic TRequestHandler
. I'm not sure if there's a better way to go about this but it was the only way I could think of to store multiple unrelated types in a single collection.
This all seems to work fine. The problem is when a request message is received. The C# code to handle request messages looks like this:
private void ProcessRequestTelegram(Telegram request)
{
var requestType = _RequestHandlers[request.MessageType].RequestType;
var typedRequest = JsonConvert.DeserializeObject(request.Payload, requestType);
var result = _RequestHandlers[request.MessageType].Handler.DynamicInvoke(typedRequest);
var jsonPayload = JsonConvert.SerializeObject(result);
var response = new Telegram()
{
Payload = jsonPayload,
CorrelationID = request.CorrelationID,
Template = TelegramTemplateEnum.Response,
SenderWindowHandle = _LocalWindowHandle.ToInt32(),
RecipientWindowHandle = _MessageRecipient.ToInt32(),
MessageType = request.MessageType
};
var jsonResponse = JsonConvert.SerializeObject(response);
var resultCode = _Messaging.SendMessage(_MessageRecipient, jsonResponse);
CheckIfSendMessageErrorOccurred(resultCode);
}
In the code above RequestType
is of the Type
type.
But for the life of me I can't come up with the Delphi equivalent. I get as far as attempting to deserialize the request.Payload and I'm stuck at how to pass the JSON parser the type to convert the payload into. I've tried various ways of storing TRequest
and TResponse
in the RequestType and ResponseType properties of TRequestHandler
: TTypeInfo
, TRTTIType
and currently TClass
. But nothing seems to give me anything useful to pass to TJson.JsonToObject
. It has a generic overload that takes a type parameter for the return type but apparently you can't use a TClass
as a type parameter.
TRequestHandler<TRequest,TResponse>
constructor is taking the types for the request and response as metaclasses. The main issue is deserializing the JSON using the metaclass.TJson.JsonToObject
doesn't expose a method that accepts a metaclass,TJsonObject
and returns an object. – Bathsheb