关于.Net Remoting中Lambda表达式的串行化问题
最近的一个项目中需要使用.Net Remoting来进行Client端与Server端的通信,其中Server端有一个数据库查询方法使用Lambda表达式作为参数,Client端将需要查询的条件以Lambda表达式的形式传给Server端运行然后返回查询结果,Server端函数如下:
{
DataClassesDataContext dc = new DataClassesDataContext(SQLiteHelper.GetConnection());
Table<T> tables = dc.GetTable<T>();
return tables.Where(expression).ToList();
}
使用时发现Linq的Expression类没有标记Serializable,所以没法串行化传递该参数。于是想找到一个可串行化对象作为中间对象,将Lambda表达式转成这个中间对象传给Server端,Server端再将其还原成Lambda表达式,最后在Server端进行查询,如图:
在网上找到了三种方法可以将Expression转换成可串行化的对象,但是使用过程中发现这些方法都有问题:
1. 使用Dynamic Expression类,本来设想使用Expression.ToString()方法将Expression转换成字符串,然后将该字符串传递给Server端,Server端再使用ParseLambda方法将字符串转换成Expression,但是发现DynamicExpression类的ParseLambda方法中的string expression变量接收的是这个类自己定义的一套类似于Lambda语法的表达式,所以Expression.ToString()方法生成的字符串无法使用ParseLambda方法将其转换成Expression对象。
{
Expression<Func<UserInfo, bool>> expression = u => u.UserName == "admin";
string expressionStr = expression.ToString();
Expression<Func<UserInfo, bool>> result = DynamicExpression.ParseLambda<UserInfo, bool>(expressionStr);
}
2. 使用Expression Tree Serialization类,该类可以将Expression串行化成XElement对象,也可以将XElement对象反串行化成Expression。
ExpressionSerializer serializer = new ExpressionSerializer();
XElement addXml = serializer.Serialize(addExpr);
Expression<Func<int,int,int>> addExpResult = serializer.Deserialize<Func<int,int,int>>(addXml);
本以为这个类可以很完美的解决.Net Remoting中Expression无法串行化的问题,但是在使用过程中发现还有下面的问题:
{
static void Main(string[] args)
{
//Test1: work!
Expression<Func<UserInfo, bool>> expression = u => u.UserName == "admin";
XElement xml = ExpressionSerializer.Serialize(expression);
Expression<Func<UserInfo, bool>> result = ExpressionSerializer.Deserialize<Func<UserInfo, bool>>(xml);
//Test2: not work!
BLL bll = new BLL("admin");
Expression<Func<UserInfo, bool>> expression1 = u => u.UserName == bll.value;
XElement xml1 = ExpressionSerializer.Serialize(expression1);
Expression<Func<UserInfo, bool>> r = ExpressionSerializer.Deserialize<Func<UserInfo, bool>>(xml1);
}
}
public class BLL
{
public string value { get; set; }
public BLL(string value)
{
this.value = value;
}
}
Test1是可以通过的,expression中的lambda表达式翻译成 u => (u.UserName = "admin"),因为"admin"是常量所以直接添加到表达式里。
Test2无法通过测试,expression1中的lambda表达式翻译成 u=> (u.UserName = TestServer.Program+<>c__DisplayClass0.value),因为value是BLL类的成员,所以就翻译成了一个类.成员的形式,但是这个方法在Deserialize的时候,就无法翻译TestServer.Program+<>c__DisplayClass0的类型,而且在Server端也无法知道BLL类的实例中value的值。
3. CodeDom, 这种方法的问题和上面的一样,也是当变量包含在一个类型中的时候,无法将真正的值变成Expression中的值。
{
//not work!
Expression<Func<UserInfo, bool>> expression = u => u.UserName == bll.value;
string expressionStr = expression.ToString();
Dictionary<string, string> providerOptions = new Dictionary<string, string>();
providerOptions.Add("CompilerVersion", "v3.5");
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerResults results = provider.CompileAssemblyFromSource
(
new CompilerParameters(new[] { "System.Core.dll" }),
@"using System;
using System.Linq.Expressions;
class foo
{
public static Expression<Func<UserInfo, bool>> GetExpression()
{
return " + expressionStr + @";
}
}"
);
Expression<Func<UserInfo, bool>> result = (Expression<Func<UserInfo, bool>>)results.CompiledAssembly.GetType("foo").GetMethod("GetExpression").Invoke(null, null);
}
上面三个方法花了一整天的时间试验以后发现都不好使,所以冒昧的发到首页想向大家请教一下,关于Lambda表达式的串行化问题大家是如何解决的?多谢^_^