【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(三)
来源:互联网 发布:java短信验证码登录 编辑:程序博客网 时间:2024/06/07 20:28
上文最后留下了一个问题,引起这个问题的操作是把原来通过硬编码字符串来设置的Expression参数改为接收用户输入。这是个非常正常的需求,可以说如果这个问题不解决,上文的Expression序列化的方法是无法应用到实际项目中的。下面来分析异常引起的原因。
首先,来查看一下接收输入来组装的Expression与硬编码的方式生成有什么不同:
1 private static void Method02() 2 { 3 Expression<Func<Member, bool>> predicate = m => m.UserName == "zhangsan"; 4 Console.WriteLine("硬编码生成的表达式:\n"+predicate); 5 Console.WriteLine("------------------------华丽的分割线--------------------------"); 6 7 var input = Console.ReadLine(); 8 predicate = m => m.UserName == input; 9 Console.WriteLine("接收输入生成的表达式:\n"+predicate);10 }
执行程序,输入“zhangsan”,运行的结果:
可以看出,接收输入生成的Expression表达式确实长得很奇异,这也解释了为什么报异常的原因。因为“Liuliu.TestConsole.Program+<>c__DisplayClass0”这个类是客户端运行时生成的,而服务端在对传过去的XElement进行反序列化时,无法识别出这个类型,自然就报错了。
怎么解决呢?通过上文中提到的KnownTypeExpressionXmlConverter类把这个类传给服务端让它变成已知类型?显然这是做不到的,因为Liuliu.TestConsole.Program+<>c__DisplayClass0这个类是运行时生成的,在运行之前根本不存在。看来此路不通。
看来只有一条路了,那就是让输入参数input与客户端分离,解除对客户端程序的依赖。而NuGet上面正好有一个叫“Dynamic Expression API”的开源组件能解决这个问题,添加引用到项目中,把上面的测试代码修改如下:
1 private static void Method02() 2 { 3 Expression<Func<Member, bool>> predicate = m => m.UserName == "zhangsan"; 4 Console.WriteLine("硬编码生成的表达式:\n"+predicate); 5 Console.WriteLine("------------------------华丽的分割线--------------------------"); 6 7 var input = Console.ReadLine(); 8 predicate = m => m.UserName == input; 9 Console.WriteLine("接收输入生成的表达式:\n"+predicate);10 Console.WriteLine("------------------------华丽的分割线--------------------------");11 12 input = Console.ReadLine();13 predicate = System.Linq.Dynamic.DynamicExpression.ParseLambda<Member, bool>("UserName=@0", input);14 Console.WriteLine("Dynamic Expression API生成的表达式:\n" + predicate);15 }
由13行可以看到,Dynamic Expression API 是使用字符串拼接的方式来生成Expression的,虽然看起来似乎是历史的倒退,但确实解除了input对客户端的依赖,看运行结果:
可以看到通过Dynamic Expression API生成的Expression与硬编码生成的完全一致,回到了原生的形式,这样,理论上把问题解决了。
回到我们的WCF,把WCF客户端代码修改如下,只需要把原来的20行修改为21行:
1 static void Main(string[] args) 2 { 3 Console.WriteLine("按任意建执行客户端调用:"); 4 Console.ReadLine(); 5 try 6 { 7 Expression<Func<Member, bool>> predicate = m => m.UserName == "zhangsan"; 8 Console.WriteLine(predicate); 9 var assemblies = new List<Assembly> { typeof(Member).Assembly, typeof(ExpressionType).Assembly, typeof(IQueryable).Assembly };10 var resolver = new TypeResolver(assemblies, new[] { typeof(Member) });11 var knownTypeConverter = new KnownTypeExpressionXmlConverter(resolver);12 var serializer = new ExpressionSerializer(resolver, new CustomExpressionXmlConverter[] { knownTypeConverter });13 var xmlPredicate = serializer.Serialize(predicate);14 var result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate));15 Console.WriteLine(result.Email);16 17 var input = Console.ReadLine();18 if (!string.IsNullOrEmpty(input))19 {20 //predicate = m => m.UserName == input;21 predicate = System.Linq.Dynamic.DynamicExpression.ParseLambda<Member, bool>("UserName=@0", input);22 Console.WriteLine(predicate);23 xmlPredicate = serializer.Serialize(predicate);24 result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate));25 Console.WriteLine(result.Email);26 }27 }28 catch (Exception e)29 {30 Console.WriteLine(e);31 }32 Console.ReadLine();33 }
运行结果与预期一致:
我们的演示项目经过几次的大手术,已经变得面目全非了,作为一个完美主义的程序员,这是不可容忍的。所以,对项目进行重构是必然的步骤。
1.客户端Client与服务实现Services中都存在 ExpressionSerializer 对象实例化的相同代码,而客户端与服务实现的一个相同点就是都引用了服务契约Contracts
所以,应该把这部分重复代码重构进Contracts中。
2.为了避免每个使用到Dynamic Expression API的地方都要对其进行引用,也应该把Dynamic Expression API封装到Contracts中。
我们把以上两个重构点封装成一个SerializeHelper静态类,使用的时候就直接调用即可。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Linq.Expressions; 5 using System.Reflection; 6 using System.Xml.Linq; 7 8 using ExpressionSerialization; 9 10 11 namespace Liuliu.Wcf.IContract.Helper12 {13 public static class SerializeHelper14 {15 16 public static Expression<Func<T, TS>> CreateExpression<T, TS>(string expression, params object[] values)17 {18 if (expression == null)19 {20 throw new ArgumentNullException("expression");21 }22 return System.Linq.Dynamic.DynamicExpression.ParseLambda<T, TS>(expression, values);23 }24 25 public static XElement SerializeExpression(Expression predicate, IEnumerable<Type> knownTypes = null)26 {27 if (predicate == null)28 {29 throw new ArgumentNullException("predicate");30 }31 var serializer = CreateSerializer(knownTypes);32 return serializer.Serialize(predicate);33 }34 35 public static XElement SerializeExpression<T, TS>(Expression<Func<T, TS>> predicate)36 {37 if (predicate == null)38 {39 throw new ArgumentNullException("predicate");40 }41 var knownTypes = new List<Type> { typeof(T) };42 var serializer = CreateSerializer(knownTypes);43 return serializer.Serialize(predicate);44 }45 46 public static Expression DeserializeExpression(XElement xmlExpression)47 {48 if (xmlExpression == null)49 {50 throw new ArgumentNullException("xmlExpression");51 }52 var serializer = CreateSerializer();53 return serializer.Deserialize(xmlExpression);54 }55 56 public static Expression<Func<T, TS>> DeserializeExpression<T, TS>(XElement xmlExpression)57 {58 if (xmlExpression == null)59 {60 throw new ArgumentNullException("xmlExpression");61 }62 var knownTypes = new List<Type> { typeof(T) };63 var serializer = CreateSerializer(knownTypes);64 return serializer.Deserialize<Func<T, TS>>(xmlExpression);65 }66 67 public static Expression<Func<T, TS>> DeserializeExpression<T, TS>(XElement xmlExpression, IEnumerable<Type> knownTypes)68 {69 if (xmlExpression == null)70 {71 throw new ArgumentNullException("xmlExpression");72 }73 var serializer = CreateSerializer(knownTypes);74 return serializer.Deserialize<Func<T, TS>>(xmlExpression);75 }76 77 private static ExpressionSerializer CreateSerializer(IEnumerable<Type> knownTypes = null)78 {79 if (knownTypes == null || !knownTypes.Any())80 {81 return new ExpressionSerializer();82 }83 var assemblies = new List<Assembly> { typeof(ExpressionType).Assembly, typeof(IQueryable).Assembly };84 knownTypes.ToList().ForEach(type => assemblies.Add(type.Assembly));85 var resolver = new TypeResolver(assemblies, knownTypes);86 var knownTypeConverter = new KnownTypeExpressionXmlConverter(resolver);87 var serializer = new ExpressionSerializer(resolver, new CustomExpressionXmlConverter[] { knownTypeConverter });88 return serializer;89 }90 }91 }
服务实现代码重构:
1 public Member GetMember(XElement xmlPredicate)2 {3 var predicate = SerializeHelper.DeserializeExpression<Member, bool>(xmlPredicate);4 return DataSource.SingleOrDefault(predicate.Compile());5 }
客户端代码重构:
1 static void Main(string[] args) 2 { 3 Console.WriteLine("按任意建执行客户端调用:"); 4 Console.ReadLine(); 5 try 6 { 7 Expression<Func<Member, bool>> predicate = m => m.UserName == "zhangsan"; 8 Console.WriteLine(predicate); 9 var xmlPredicate = SerializeHelper.SerializeExpression(predicate);10 var result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate));11 Console.WriteLine(result.Email);12 13 var input = Console.ReadLine();14 if (!string.IsNullOrEmpty(input))15 {16 predicate = SerializeHelper.CreateExpression<Member, bool>("UserName=@0", input);17 xmlPredicate = SerializeHelper.SerializeExpression(predicate);18 result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate));19 Console.WriteLine(result.Email);20 }21 }22 catch (Exception e)23 {24 Console.WriteLine(e);25 }26 Console.ReadLine();27 }
服务端代码无变化。重构之后,一切都变得井然有序,生活多么美好。
至此,Expression表达式的远程传输中序列化的问题得到了比较圆满的解决,但是使用Dynamic Expression API生成表达式的步骤失去了原来Lambada表达式的优势,返祖了。
所以,这只是一个比较初级的解决方案,希望能有更优良的解决方案来保持Lambada表达式的优势。
第一次写博客,结构比较凌乱,基本上是按我解决问题的思路流水下来的,不够清晰,敬请大家谅解……
最后,奉上本文涉及的源代码:
LambadaSerializeDemo03(重构前).rar
LambadaSerializeDemo03(重构后).rar
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(三)
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(一)
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(二)
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(四)——关于Guid的问题
- WCF+Nhibernate 序列化的问题
- WCF+Nhibernate循环引用导致序列化的问题
- Entity Framework在WCF中序列化的问题
- Entity Framework在WCF中序列化的问题
- Entity Framework在WCF中序列化的问题 学习
- Entity Framework在WCF中序列化的问题
- 使用css expression遇到的一个问题
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- 在 WCF 中使用高效的 BinaryFormatter 序列化
- 修改WCF的默认序列化格式
- constant expression required 的问题
- Map的keySet方法序列化BUG问题及其解决方法
- K:枚举的线程安全性及其序列化问题
- DB2数据库,执行存储过程报错 SQLSTATE=22003
- CSS之解决Div背景颜色了渐变问题
- oracle数据库管理基础知识
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(二)
- SQL方式实现SQLite增删改查
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(三)
- 在线域名查ip网站
- Set up start development environment on ubuntu-12.04.1
- HttpServletRequest与HttpServletResponse
- 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(四)——关于Guid的问题
- Perl抓取网页信息
- android精品开源项目
- 做管理者要让自己对具体业务细节多说不知道
- java.net.UnknownHostException 异常处理