最近的一个项目中需要使用.Net Remoting来进行Client端与Server端的通信,其中Server端有一个数据库查询方法使用Lambda表达式作为参数,Client端将需要查询的条件以Lambda表达式的形式传给Server端运行然后返回查询结果,Server端函数如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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对象。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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无法串行化的问题,但是在使用过程中发现还有下面的问题:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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中的值。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
// 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表达式的串行化问题大家是如何解决的?多谢^_^