Search code examples
c#expression-treesdynamic-invoke

Build expression tree for method decoration?


I’m building a class named CommandHandler that have ExecuteCommand method that have Command as input parameter.

The Idea is that ExcecuteCommand will check command name and execute proper class method by the name pattern so when command name is Test class should have corresponding TestHandler method.

On initialization I’m using reflection to find all methods and create a mapping between command name and created Func<Command, Task<object>>. For now I have achieve this by making all methods return Task<object> and use Delegate.CreateDelegate to create Func<Command, Task<object>> from reflected method but I would like to clean code and allow methods to return simple types like int or Task<custom class>.

I would like to build some simple Expression that for simple types will execute method and do Task.FromResult dynamically so class method remains clean. For Task with specific type result I would like to create expression that will result Task<object> so all could be cached as Dictionary<string, Func<Command, Task<object>>.

 public class CommandHandler
    {
        public Dictionary<string, Func<Command, Task<object>>> methodCache = new Dictionary<string, Func<Command, Task<object>>>();

        public int IntCommandHandler(Command c)
        {
            return 5;
        }

        public string StringCommandHandler(Command c)
        {
            return "5";
        }

        public Task<int> AsyncIntCommandHandler(Command c)
        {
            return Task.Run(() => 5);
        }

        public async Task<object> OldWayCommandHandler(Command c)
        {
            return "5";
        }

        private void RegisterAsyncQueryHandlers(Dictionary<string, MethodInfo> handlers)
        {
            var filtered = handlers.Where(h => h.Value.ReturnType == typeof(Task<object>)).ToList();
            foreach (var handler in filtered)
            {
                methodCache.Add(handler.Key, (Func<Command, Task<object>>)Delegate.CreateDelegate(typeof(Func<Command, Task<object>>), this, handler.Value, false));
            }
        }

        public void FillCache()
        {
            // Get all methods with proper pattern and pass it to RegisterAsyncQueryHandlers in dictionary of command name and MethodInfo
            //RegisterAsyncQueryHandlers
        }

        public Task<object> ExecuteCommand(Command c)
        {
            return methodCache[c.Name].Invoke(c);
        }
    }

    public class Command
    {
        public string Name { get; set; }
    }

I have no experience with using Expressions and most of the samples I found are using basic operators and static methods. Maybe someone can help me ho w to build such expression?


Solution

  • If I understand correctly, the question is how to convert Task<TResult> to Task<object>.

    It can be done for instance using the Task<TResult>.ContinueWith method as follows:

    static Task<object> Convert<TResult>(Task<TResult> source)
    {
        return source.ContinueWith(t => (object)t.Result);
    }
    

    It's possible to dynamically build such expression, but the easier would be to put the above method in your class and "call" it via the following Expression.Call overload.

    The method that builds expression and compiles a delegate from it could be like this:

    Func<Command, Task<object>> MakeFunc(MethodInfo handler)
    {
        var c = Expression.Parameter(typeof(Command), "c");
        var task = Expression.Call(Expression.Constant(this), handler, c);
        if (task.Type != typeof(Task<object>))
            task = Expression.Call(GetType(), "Convert", new[] { task.Type.GetGenericArguments().Single() }, task);
        var expr = Expression.Lambda<Func<Command, Task<object>>>(task, c);
        return expr.Compile();
    }