I am having an issue simplifying some code. I've spent about 3 or 4 hours researching and trialing different ways to do it but ultimately I haven't found what I need and I'm not even sure if what I'm trying to do is possible.
Here is the current version of the code (simplified a bit for readability).
public class PrototypeHandler : HandlerBase, IPrototypeHandler
{
public override void RegisterHandlers(HubConnection activeHubConnection)
{
base.RegisterHandlers(activeHubConnection);
_activeHubConnection.On("SendSingleMessageToUser", (Func<MessageDetails, ReturnType>)SendSingleMessageToUser);
_activeHubConnection.On("SendSingleMessage", (Action<MessageDetails>)SendSingleMessage);
}
public ReturnType SendSingleMessageToUser(MessageDetails messageDetails)
{
return new ReturnType();
}
public void SendSingleMessage(MessageDetails messageDetails)
{
return;
}
}
PrototypeHandler
is a prototype for a pattern I'd like to establish in my codebase for a bunch of handler classes, each with several methods that need to be registered with a SignalR HubConnection
. In this code, each handler is responsible for registering its own methods with the HubConnection
using the RegisterHandlers
method. What I'd like to do is to make this dynamic so that I can put the RegisterHandlers
method in the HandlerBase
. Here is my vision for that
In PrototypeHandler
:
public class HandlerAttribute : Attribute
{
public string MethodName { get; set; }
}
public class PrototypeHandler : HandlerBase, IPrototypeHandler
{
[Handler(MethodName = "SendSingleMessageToUser")]
public ReturnType SendSingleMessageToUser(MessageDetails messageDetails)
{
return new ReturnType();
}
[Handler(MethodName = "SendSingleMessageToUser")]
public void SendSingleMessage(MessageDetails messageDetails)
{
return;
}
}
And in HandlerBase
:
public void RegisterHandlers(HubConnection activeHubConnection)
{
var methods =
from m in this.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
let handlerAttribute = m.GetCustomAttribute(typeof(HandlerAttribute))
where handlerAttribute != null
select new { Method = m, HandlerAttribute = (HandlerAttribute)handlerAttribute };
foreach (var current in methods)
{
// Call the HubConnection's "On" method to register
// this method as a handler
}
}
The problem is, there are so many overloads to the On method in the HubConnection
that support different types of delegates being passed. I want to support all of them so as to not limit our ability for the types of method signatures we can support in the handlers. Here is a list of all of the overloads for On
that I'd like to support.
public static IDisposable On(this HubConnection hubConnection, string methodName, Action handler);
public static IDisposable On<T1, T2, T3, T4, T5, T6, T7>(this HubConnection hubConnection, string methodName, Func<T1, T2, T3, T4, T5, T6, T7, Task> handler);
public static IDisposable On<T1, T2, T3, T4, T5, T6, T7, T8>(this HubConnection hubConnection, string methodName, Func<T1, T2, T3, T4, T5, T6, T7, T8, Task> handler);
public static IDisposable On<T1>(this HubConnection hubConnection, string methodName, Action<T1> handler);
public static IDisposable On<T1, T2>(this HubConnection hubConnection, string methodName, Action<T1, T2> handler);
public static IDisposable On<T1, T2, T3>(this HubConnection hubConnection, string methodName, Action<T1, T2, T3> handler);
public static IDisposable On<T1, T2, T3, T4>(this HubConnection hubConnection, string methodName, Action<T1, T2, T3, T4> handler);
public static IDisposable On<T1, T2, T3, T4, T5>(this HubConnection hubConnection, string methodName, Action<T1, T2, T3, T4, T5> handler);
public static IDisposable On<T1, T2, T3, T4, T5, T6, T7>(this HubConnection hubConnection, string methodName, Action<T1, T2, T3, T4, T5, T6, T7> handler);
public static IDisposable On<T1, T2, T3, T4, T5, T6, T7, T8>(this HubConnection hubConnection, string methodName, Action<T1, T2, T3, T4, T5, T6, T7, T8> handler);
public static IDisposable On<T1, T2, T3, T4, T5, T6>(this HubConnection hubConnection, string methodName, Action<T1, T2, T3, T4, T5, T6> handler);
public static IDisposable On(this HubConnection hubConnection, string methodName, Func<Task> handler);
public static IDisposable On<T1>(this HubConnection hubConnection, string methodName, Func<T1, Task> handler);
public static IDisposable On<T1, T2>(this HubConnection hubConnection, string methodName, Func<T1, T2, Task> handler);
public static IDisposable On<T1, T2, T3>(this HubConnection hubConnection, string methodName, Func<T1, T2, T3, Task> handler);
public static IDisposable On<T1, T2, T3, T4>(this HubConnection hubConnection, string methodName, Func<T1, T2, T3, T4, Task> handler);
public static IDisposable On<T1, T2, T3, T4, T5>(this HubConnection hubConnection, string methodName, Func<T1, T2, T3, T4, T5, Task> handler);
public static IDisposable On<T1, T2, T3, T4, T5, T6>(this HubConnection hubConnection, string methodName, Func<T1, T2, T3, T4, T5, T6, Task> handler);
public static IDisposable On(this HubConnection hubConnection, string methodName, Type[] parameterTypes, Func<object[], Task> handler);
As you can see, there are overloads that take Action
, Action<T1>
, Action<T1, T2>
and so on as well as the same for Func i.e. Func<T1, Task>
, Func<T1, T2, Task>
etc.
I have tried a lot of different things to get this to work but am not having success. At first I tried to just get a reference to the current.Method
object as a delegate in my loop, cast it to the correct delegate type, e.g. System.Action`1[MessageDetails]
and then call the On method directly, but the compiler couldn't be convinced that the cast delegate would be the correct type for any On
overloads.
After that I resorted to using reflection to call the On
method. That failed at runtime because it would say that System.Action`1[MessageDetails]
isn't the same as System.Action<T1>
, for example, but I also had trouble finding the correct On
method to invoke.
Here is the most recent code I have. It is incomplete where I would find and invoke the correct On
method for the HubConnection.
public void RegisterHandlers(HubConnection activeHubConnection)
{
var methods =
from m in this.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
let handlerAttribute = m.GetCustomAttribute(typeof(HandlerAttribute))
where handlerAttribute != null
select new { Method = m, HandlerAttribute = (HandlerAttribute)handlerAttribute };
foreach (var current in methods)
{
// Get a delegate type (e.g. Action, Func<T>) that matches this method type
Type delegateType = Expression.GetDelegateType(current.Method.GetParameters().Select(p => p.ParameterType).Append(current.Method.ReturnType).ToArray());
// Find the correct "On" method to invoke
MethodInfo onMethod = typeof(HubConnectionExtensions).GetType().GetMethod(nameof(HubConnection.On), /*Need to find which parameters to pass to get correct "On" method*/);
// Invoke the "On" method with correct parameters
onMethod.Invoke(activeHubConnection, new object[] { current.HandlerAttribute.MethodName, /*missing parameters here*/ });
}
}
I appreciate any help someone might be able to provide.
Aucun commentaire:
Enregistrer un commentaire