Here is another approach using the idea I've mentioned in the other post. It's actually very simple to split the Crack method in two methods and have OnMessageTo and OnMessageFrom handlers. The modified implementation of the MessageCracker helper class above is aware about the message direction, though certainly can be improved.
In your Application, implement like this (not complete code):
public class MyFixApplication: DirectedMessageCracker, Application
{
...
public void FromAdmin(Message msg, SessionID sessionId)
{
CrackFrom(msg, sessionId);
}
public void ToAdmin(Message msg, SessionID sessionId)
{
CrackTo(msg, sessionId);
}
public void OnMessageTo(Logon msg, SessionID sessionId)
{
//Treat the outgoing message, set user, password, etc
}
public void OnMessageFrom(Allocation msg, SessionID sessionId)
{
//Treat the incoming Allocation message
}
...and so on
And the modified MessageCracker:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace QuickFix
{
/// <summary>
/// Helper class for delegating message types for various FIX versions to
/// type-safe OnMessage methods, supports handling of incoming and outgoing messages separatelly
/// </summary>
public abstract class DirectedMessageCracker
{
private readonly Dictionary<Type, MethodInfo> _toHandlerMethods = new Dictionary<Type, MethodInfo>();
private readonly Dictionary<Type, MethodInfo> _fromHandlerMethods = new Dictionary<Type, MethodInfo>();
protected DirectedMessageCracker()
{
Initialize(this);
}
private void Initialize(Object messageHandler)
{
var handlerType = messageHandler.GetType();
var methods = handlerType.GetMethods();
foreach (var m in methods)
{
if (IsToHandlerMethod(m))
_toHandlerMethods[m.GetParameters()[0].ParameterType] = m;
else if (IsFromHandlerMethod(m))
_fromHandlerMethods[m.GetParameters()[0].ParameterType] = m;
}
}
static public bool IsToHandlerMethod(MethodInfo m)
{
return IsHandlerMethod("OnMessageTo", m);
}
static public bool IsFromHandlerMethod(MethodInfo m)
{
return IsHandlerMethod("OnMessageFrom", m);
}
static public bool IsHandlerMethod(string searchMethodName, MethodInfo m)
{
return (m.IsPublic
&& m.Name.StartsWith(searchMethodName)
&& m.GetParameters().Length == 2
&& m.GetParameters()[0].ParameterType.IsSubclassOf(typeof(Message))
&& typeof(SessionID).IsAssignableFrom(m.GetParameters()[1].ParameterType)
&& m.ReturnType == typeof(void));
}
/// <summary>
/// Process ("crack") a FIX message and call the registered handlers for that type, if any
/// </summary>
/// <param name="handlerMethods"></param>
/// <param name="message"></param>
/// <param name="sessionID"></param>
private void Crack(IDictionary<Type, MethodInfo> handlerMethods, Message message, SessionID sessionID)
{
var messageType = message.GetType();
MethodInfo handler;
if (handlerMethods.TryGetValue(messageType, out handler))
handler.Invoke(this, new object[] { message, sessionID });
else
throw new UnsupportedMessageType();
}
/// <summary>
/// Process ("crack") an INCOMING FIX message and call the registered handlers for that type, if any
/// </summary>
/// <param name="message"></param>
/// <param name="sessionID"></param>
public void CrackFrom(Message message, SessionID sessionID)
{
Crack(_fromHandlerMethods, message, sessionID);
}
/// <summary>
/// Process ("crack") an OUTGOING FIX message and call the registered handlers for that type, if any
/// </summary>
/// <param name="message"></param>
/// <param name="sessionID"></param>
public void CrackTo(Message message, SessionID sessionID)
{
Crack(_toHandlerMethods, message, sessionID);
}
}
}
You can also skip the crack instead of throwing an exception in case of you don't want to implement all possible message handlers by just removing the throw new UnsupportedMessageType();
from the crack method.
Another idea is to split cracking Admin/App messages.