Question

Edit2: removed a load of gubbins + bounty.

I have deconstructed an expression for a message bus in the hope of reconstructing it and invoking it in a slightly different manner. The serialization and deserialization is successful and I am able to create instances of most of what I need.

//Deconstruct
Expression<Func<T, Task>> expression

proxy => proxy.serviceMethod(arg);

I need to create the syntax below. T is an interface to a WCF service. This expression will be passed to a service invoker where it's internal ChannelFactory will pass it into this method.

//Reconstruct this as expression so I can pass it as a parameter
var myAction = new Action<T>(proxy => {
    proxy.serviceMethod((SomeType)SomeParameter));
});

// to pass to this method
serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] { myAction });

What I have:

//I think i'm nearly there I can create the inner call and assign 
//the correct parameter, but I can't seem to figure out how to wrap it in an 
// new Action<serviceT> { my other expressions... }

// Types
var serviceT = Type.GetType(workOutMessage.interfaceType);
var actionT = typeof(Action<>).MakeGenericType(serviceT);
var envelopeT = Type.GetType(workOutMessage.methodArgTypes[0]);

// ServiceInvoker<T> Instantiation - Works
var serviceInvokerT = typeof(HubServiceInvoker<>).MakeGenericType(serviceT);
var serviceInvokerTMethod = serviceInvokerT.GetMethod("InvokeService");
var serviceInvokerTInstance = Activator.CreateInstance(serviceInvokerT, client.Id, "password", clientCert, serviceCert);

// Expression Type Params
var serviceTParam = Expression.Parameter(serviceT, "proxy");
var envelopeParam = Expression.Parameter(envelopeT, "envelope");

var envAssign = Expression.Assign(envelopeParam, Expression.Constant(workOutMessage.methodArgs[0]));
var methodCall = Expression.Call(serviceTParam, serviceT.GetMethod(workOutMessage.methodName), envelopeParam);

// var lambda = ...                                               
// make new Action<serviceT> myAction = { proxy => proxy.someMethod(someParameter); };

serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] { lambda.Compile() });  

Edit: The Service Invoker Method I pass this into to, to try and give the problem better context.

public void InvokeService(Action<T> handler)
    {
        T proxy = channelFactory.CreateChannel();
        ((IClientChannel)proxy).Faulted += ChannelFaulted;

        ICommunicationObject obj2 = (ICommunicationObject)proxy;
        try
        {
            using (new OperationContextScope((IContextChannel)proxy))
            {
                handler.Invoke(proxy);
            }
        }
        finally
        {
            try
            {
                if (obj2.State != CommunicationState.Faulted)
                {
                    obj2.Close();
                }
            }
            catch
            {
                obj2.Abort();
            }
        }
    }
Was it helpful?

Solution 3

Thanks to the hints from @Romain Hautefeuille, the key was to use my generic ServiceInvoker class to help me create the action that I needed without the need of using Expressions (woohoo).

// Execute Interface method from interface, methodName and methodArgs from message queue

var serviceT = Type.GetType(workOutMessage.interfaceType);
var serviceInvokerT = typeof(HubServiceInvoker<>).MakeGenericType(serviceT);
var serviceInvokerTMethod = serviceInvokerT.GetMethod("InvokeService");
var serviceInvokerCompileTMethod = serviceInvokerT.GetMethod("CompileServiceMethod");
var serviceInvokerTInstance = Activator.CreateInstance(serviceInvokerT, client.Id, 
                                                    "password", clientCert, serviceCert);

// Works! and a lot simpler
serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] {
    workOutMessage.correlationId,
    serviceInvokerCompileTMethod.Invoke(serviceInvokerTInstance, new object[] { 
                workOutMessage.methodName, 
                workOutMessage.methodArgs })
});

And Finally the new method in the ServiceInvoker class (I wanted to avoid reflection in this class - for no particular reason - but it doesn't affect calling it normally).

 public Action<T> CompileServiceMethod(string methodName, object[] methodArguments)
    {
        return new Action<T>(proxy =>
        {
            typeof(T).GetMethod(methodName).Invoke(proxy, methodArguments);
        });
    }  

OTHER TIPS

Here is a full piece of code, in which I assume you only need a Func, not an Action.

using System;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace HelloWorld
{
    public class Service1
    {
        public Task ServiceMethod(string something)
        {
            return Task.Factory.StartNew(() => Console.WriteLine(something));
        }
    }

    public class HubServiceInvoker<T> where T : new()
    {
        T t;

        public HubServiceInvoker(string id, string password)
        {
            t = new T();
        }

        public void InvokeService(Func<T, Task> serviceInvoker)
        {
            Task task = serviceInvoker(t);
        }

        public static Func<T, Task> CompileInvoker(Expression expression, ParameterExpression serviceTParam)
        {
            Expression<Func<T, Task>> lambda = Expression.Lambda<Func<T, Task>>(expression, serviceTParam);
            return lambda.Compile();
        }
    }

    public class WorkOutMessage
    {
        public string interfaceType { get; set; }
        public string[] methodArgTypes { get; set; }
        public object[] methodArgs { get; set; }
        public string methodName { get; set; }
    }

    static class Program
    {
        static void Main(string[] args)
        {
            WorkOutMessage workOutMessage = new WorkOutMessage()
            {
                interfaceType = "HelloWorld.Service1",
                methodArgTypes = new string[] { "System.String" },
                methodArgs = new object[] { "yeah it works!" },
                methodName = "ServiceMethod"
            };

            InvokeService(workOutMessage);

            Console.Read();
        }

        static void InvokeService(WorkOutMessage workOutMessage)
        {
            // Types
            var serviceT = Type.GetType(workOutMessage.interfaceType);

            // ServiceInvoker<T> Instantiation - Works
            var serviceInvokerT = typeof(HubServiceInvoker<>).MakeGenericType(serviceT);
            var serviceInvokerTMethod = serviceInvokerT.GetMethod("InvokeService");
            var serviceCompileInvokerTMethod = serviceInvokerT.GetMethod("CompileInvoker");
            var serviceInvokerTInstance = Activator.CreateInstance(serviceInvokerT, "id", "password");

            // Expression Type Params
            var serviceTParam = Expression.Parameter(serviceT, "proxy");

            var methodCall = Expression.Call(serviceTParam, serviceT.GetMethod(workOutMessage.methodName), Expression.Constant(workOutMessage.methodArgs[0]));

            serviceInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] { 
                serviceCompileInvokerTMethod.Invoke(serviceInvokerTInstance, new object[] { methodCall, serviceTParam })
            });
        }
    }
}

It was a little bit hard to understand what exactly is going on.

Perhaps, this will help you a little bit further:

        // Helper.cs
        public static Action<TType> Wrap<TType>(Delegate test)
        {
            return ret => test.DynamicInvoke();
        }

       var meth = typeof(Helper).GetMethod("Wrap");
       var gmeth = meth.MakeGenericMethod(new[] { serviceT });
       var genericAction = gmeth.Invoke(null, new object[] { 
                Expression.Lambda(methodCall).Compile(); });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top