为新Action <t>创建表达式,其中T在编译时是未知的</t>

时间:2014-05-02 13:21:32

标签: c# .net linq linq-expressions

Edit2:删除了一大堆gubbins + bounty。

我已经解构了一个消息总线的表达式,希望能够重构它并以稍微不同的方式调用它。序列化和反序列化是成功的,我能够创建我需要的大部分实例。

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

proxy => proxy.serviceMethod(arg);

我需要在下面创建语法。 T是WCF服务的接口。此表达式将传递给服务调用程序,其中内部ChannelFactory将其传递给此方法。

//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 });

我有什么:

//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() });  

编辑:服务调用者方法我将其传递给,以尝试为问题提供更好的上下文。

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();
            }
        }
    }

3 个答案:

答案 0 :(得分:3)

这是一段完整的代码,我假设你只需要一个Func,而不是一个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 })
            });
        }
    }
}

答案 1 :(得分:1)

有点难以理解到底发生了什么。

也许,这会对你有所帮助:

        // 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(); });

答案 2 :(得分:0)

感谢来自@Romain Hautefeuille的提示,关键是使用我的通用ServiceInvoker类来帮助我创建我需要的动作,而无需使用表达式(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 })
});

最后是ServiceInvoker类中的新方法(我想避免在这个类中反映 - 没有特别的原因 - 但它不会影响它的正常调用)。

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