【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(二)
来源:互联网 发布:java短信验证码登录 编辑:程序博客网 时间:2024/06/14 21:55
接上文 【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(一)
上文留下了一个问题没有处理,但最后也找到了相应的解决方案,下面就来说下问题的解决
Expression Tree Serializer 提供的解决方案是把Expression表达式树转换为XElement类型的XML数据,传输到服务端,再反转换还原成原来的Expression表达式
所以,客户端与服务端之间传送的数据是XElement类型的数据了,从而避开了Expression类型不能序列化的问题
我们先来了解一下Expression Tree Serializer的使用,下载 Expression Tree Serializer 源代码进行编译,只需要用到其中的ExpressionSerialization.dll
新建一个控制台项目,添加ExpressionSerialization引用。先来试用一下ExpressionSerialization对Expression的处理
把Main方法修改为如下代码:
1 static void Main(string[] args) 2 { 3 var sources = new[] { "abc", "abd", "bcd", "acd", "cdb" }; 4 Expression<Func<string, bool>> predicate = m => m.StartsWith("a"); 5 Console.WriteLine("使用原始Expression表达式查询数据:"); 6 Console.WriteLine(predicate); 7 sources.Where(predicate.Compile()).ToList().ForEach(s => Console.Write(s + " ")); 8 Console.WriteLine(); 9 var serializer = new ExpressionSerializer();10 var xmlPredicate = serializer.Serialize(predicate);11 var newPredicate = serializer.Deserialize<Func<string, bool>>(xmlPredicate);12 Console.WriteLine("使用新Expression表达式查询数据:");13 Console.WriteLine(newPredicate);14 sources.Where(newPredicate.Compile()).ToList().ForEach(s => Console.Write(s + " "));15 Console.ReadLine();16 }
注意:如提示无法生成请将控制台项目的“目标框架”由原来的“.NET Framework 4 Client Profie”修改为“.NET Framework 4”
运行控制台项目,得到如下结果,与预期一致:
有了成功的第一步,就敢大踏步往下走了,马上对上一文中的代码进行手术
对Services与Client项目分别添加ExpressionSerialization引用
WCF服务契约代码修改为如下:
1 [ServiceContract]2 public interface IAccountContract3 {4 [OperationContract]5 Member GetMember(XElement xmlPredicate);6 }
WCF服务实现代码修改如下,为了在客户端显示服务端发生的异常信息,在服务实现类添加[ServiceBehavior(IncludeExceptionDetailInFaults =true)]特性:
1 [ServiceBehavior(IncludeExceptionDetailInFaults = true)] 2 public class AccountService : IAccountContract 3 { 4 private readonly static List<Member> DataSource = new List<Member> 5 { 6 new Member {MemberID = 3, UserName = "zhangsan", Email = "zhangsan@abc.com"}, 7 new Member {MemberID = 4, UserName = "lisi", Email = "lisi@abc.com"}, 8 new Member {MemberID = 5, UserName = "wangwu", Email = "wangwu@abc.com"}, 9 new Member {MemberID = 6, UserName = "zhaoliu", Email = "zhaoliu@abc.com"}10 };11 12 public Member GetMember(XElement xmlPredicate)13 {14 var serializer = new ExpressionSerializer();15 var predicate = serializer.Deserialize<Func<Member, bool>>(xmlPredicate);16 return DataSource.SingleOrDefault(predicate.Compile());17 }18 }
客户端代码修改如下:
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 var serializer = new ExpressionSerializer(); 9 var xmlPredicate = serializer.Serialize(predicate);10 var result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate));11 Console.WriteLine(result.Email);12 }13 catch (Exception e)14 {15 Console.WriteLine(e);16 }17 Console.ReadLine();18 }
再次注意:如提示无法生成请将控制台项目的“目标框架”由原来的“.NET Framework 4 Client Profie”修改为“.NET Framework 4”
我们再次信心满满的修改多项目启动,运行……服务端与客户端都如愿正常启动了:
但是,客户端按任意键执行调用的时候……客户端发生了如下异常:
捕捉到 System.ServiceModel.FaultException<System.ServiceModel.ExceptionDetail> Message=Could not find a matching type参数名: Liuliu.Wcf.IContract.Member Source=mscorlib Action=http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault StackTrace: Server stack trace: 在 System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter) 在 System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc) 在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) 在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) 在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) 在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown at [0]: 在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) 在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) 在 Liuliu.Wcf.IContract.IAccountContract.GetMember(XElement xmlPredicate) 在 Liuliu.Wcf.Client.Program.<>c__DisplayClass1.<Main>b__0(IAccountContract wcf) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.Client\Program.cs:行号 25 在 Liuliu.Wcf.IContract.Helper.WcfHelper.InvokeService[TContract,TReturn](Func`2 func) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.IContract\Helper\WcfHelper.cs:行号 30 在 Liuliu.Wcf.Client.Program.Main(String[] args) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.Client\Program.cs:行号 25 InnerException:
Liuliu.Wcf.IContract.Member类无法识别?!从客户端传过去的是数据是XElement的XML数据,因而ExpressionSerializer在进行反序列化操作的时候不知道有Liuliu.Wcf.IContract.Member这个类,知道大概也是只知其名而已
那就手动让ExpressionSerializer去详细了解Liuliu.Wcf.IContract.Member吧,万般查找发现了KnownTypeExpressionXmlConverter这个类,看名就知道,就是干这个使的。
修改服务实现代码如下:
1 public Member GetMember(XElement xmlPredicate)2 {3 var assemblies = new List<Assembly> {typeof(Member).Assembly, typeof(ExpressionType).Assembly, typeof(IQueryable).Assembly};4 var resolver = new TypeResolver(assemblies, new[] {typeof(Member)});5 var knownTypeConverter = new KnownTypeExpressionXmlConverter(resolver);6 var serializer = new ExpressionSerializer(resolver, new CustomExpressionXmlConverter[] { knownTypeConverter });7 var predicate = serializer.Deserialize<Func<Member, bool>>(xmlPredicate);8 return DataSource.SingleOrDefault(predicate.Compile());9 }
客户端代码修改如下:
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 var assemblies = new List<Assembly> { typeof(Member).Assembly, typeof(ExpressionType).Assembly, typeof(IQueryable).Assembly }; 9 var resolver = new TypeResolver(assemblies, new[] { typeof(Member) });10 var knownTypeConverter = new KnownTypeExpressionXmlConverter(resolver);11 var serializer = new ExpressionSerializer(resolver, new CustomExpressionXmlConverter[] { knownTypeConverter });12 var xmlPredicate = serializer.Serialize(predicate);13 var result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate));14 Console.WriteLine(result.Email);15 }16 catch (Exception e)17 {18 Console.WriteLine(e);19 }20 Console.ReadLine();21 }
再次运行,报如下异常:
捕捉到 System.ServiceModel.FaultException<System.ServiceModel.ExceptionDetail> Message=序列不包含任何元素 Source=mscorlib Action=http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault StackTrace: Server stack trace: 在 System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter) 在 System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc) 在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) 在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) 在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) 在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown at [0]: 在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) 在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) 在 Liuliu.Wcf.IContract.IAccountContract.GetMember(XElement xmlPredicate) 在 Liuliu.Wcf.Client.Program.<>c__DisplayClass2.<Main>b__1(IAccountContract wcf) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.Client\Program.cs:行号 29 在 Liuliu.Wcf.IContract.Helper.WcfHelper.InvokeService[TContract,TReturn](Func`2 func) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.IContract\Helper\WcfHelper.cs:行号 30 在 Liuliu.Wcf.Client.Program.Main(String[] args) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.Client\Program.cs:行号 29 InnerException:
又查找了半天,没发现原因,想想会不会是数据的发送与接收不一致了,加了个在客户端发送前与服务端接收后都把相应的XElement数据写入文本文件中,一对比,居然真的不一样,吭爹啊……
2.12KB VS 2.23KB
<LambdaExpression NodeType="Lambda" Name="" TailCall="false" CanReduce="false"> <Type> <Type Name="System.Func`2"> <Type Name="Liuliu.Wcf.IContract.Member" /> <Type Name="System.Boolean" /> </Type> </Type> <Parameters> <ParameterExpression NodeType="Parameter" Name="m" IsByRef="false" CanReduce="false"> <Type> <Type Name="Liuliu.Wcf.IContract.Member" /> </Type> </ParameterExpression> </Parameters> <Body> <BinaryExpression CanReduce="false" IsLifted="false" IsLiftedToNull="false" NodeType="Equal"> <Right> <ConstantExpression NodeType="Constant" CanReduce="false"> <Type> <Type Name="System.String" /> </Type> <Value>zhangsan</Value> </ConstantExpression> </Right> <Left> <MemberExpression NodeType="MemberAccess" CanReduce="false"> <Member MemberType="Property" PropertyName="UserName"> <DeclaringType> <Type Name="Liuliu.Wcf.IContract.Member" /> </DeclaringType> <IndexParameters /> </Member> <Expression> <ParameterExpression NodeType="Parameter" Name="m" IsByRef="false" CanReduce="false"> <Type> <Type Name="Liuliu.Wcf.IContract.Member" /> </Type> </ParameterExpression> </Expression> <Type> <Type Name="System.String" /> </Type> </MemberExpression> </Left> <Method MemberType="Method" MethodName="op_Equality"> <DeclaringType> <Type Name="System.String" /> </DeclaringType> <Parameters> <Type> <Type Name="System.String" /> </Type> <Type> <Type Name="System.String" /> </Type> </Parameters> <GenericArgTypes /> </Method> <Conversion /> <Type> <Type Name="System.Boolean" /> </Type> </BinaryExpression> </Body> <ReturnType> <Type Name="System.Boolean" /> </ReturnType></LambdaExpression>
<LambdaExpression NodeType="Lambda" Name="" TailCall="false" CanReduce="false" xmlns=""> <Type> <Type Name="System.Func`2"> <Type Name="Liuliu.Wcf.IContract.Member"></Type> <Type Name="System.Boolean"></Type> </Type> </Type> <Parameters> <ParameterExpression NodeType="Parameter" Name="m" IsByRef="false" CanReduce="false"> <Type> <Type Name="Liuliu.Wcf.IContract.Member"></Type> </Type> </ParameterExpression> </Parameters> <Body> <BinaryExpression CanReduce="false" IsLifted="false" IsLiftedToNull="false" NodeType="Equal"> <Right> <ConstantExpression NodeType="Constant" CanReduce="false"> <Type> <Type Name="System.String"></Type> </Type> <Value>zhangsan</Value> </ConstantExpression> </Right> <Left> <MemberExpression NodeType="MemberAccess" CanReduce="false"> <Member MemberType="Property" PropertyName="UserName"> <DeclaringType> <Type Name="Liuliu.Wcf.IContract.Member"></Type> </DeclaringType> <IndexParameters></IndexParameters> </Member> <Expression> <ParameterExpression NodeType="Parameter" Name="m" IsByRef="false" CanReduce="false"> <Type> <Type Name="Liuliu.Wcf.IContract.Member"></Type> </Type> </ParameterExpression> </Expression> <Type> <Type Name="System.String"></Type> </Type> </MemberExpression> </Left> <Method MemberType="Method" MethodName="op_Equality"> <DeclaringType> <Type Name="System.String"></Type> </DeclaringType> <Parameters> <Type> <Type Name="System.String"></Type> </Type> <Type> <Type Name="System.String"></Type> </Type> </Parameters> <GenericArgTypes></GenericArgTypes> </Method> <Conversion></Conversion> <Type> <Type Name="System.Boolean"></Type> </Type> </BinaryExpression> </Body> <ReturnType> <Type Name="System.Boolean"></Type> </ReturnType></LambdaExpression>
对比以上数据可发现,
所有的空标签发送时表示为<empty />的形式,而在接收后表示为<empty></empty>的形式,而ExpressionSerialization并没有对这种情况进行处理
找到的问题所在,就能把问题解决掉
将ExpressionSerialization工程的ExpressionSerializer(Deserialize).cs 文件中的“if (xml.IsEmpty)”语句替换为“if (xml.IsEmpty || !xml.Elements().Any())”,共有3处,然后重新编译ExpressionSerialization,添加引用
再次运行程序,终于得到期待已久的结果了:
现在可以高歌:红军不怕远征难,万水千山只等闲……
目前为此基本上解决了Expression序列化的问题了,可是……某日突然报了这么个异常:
捕捉到 System.ServiceModel.FaultException<System.ServiceModel.ExceptionDetail> Message=值不能为 null。参数名: typeName Source=mscorlib Action=http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher/fault StackTrace: Server stack trace: 在 System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter) 在 System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc) 在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) 在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs) 在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) 在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message) Exception rethrown at [0]: 在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) 在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) 在 Liuliu.Wcf.IContract.IAccountContract.GetMember(XElement xmlPredicate) 在 Liuliu.Wcf.Client.Program.<>c__DisplayClass4.<Main>b__2(IAccountContract wcf) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.Client\Program.cs:行号 37 在 Liuliu.Wcf.IContract.Helper.WcfHelper.InvokeService[TContract,TReturn](Func`2 func) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.IContract\Helper\WcfHelper.cs:行号 30 在 Liuliu.Wcf.Client.Program.Main(String[] args) 位置 D:\我的文档\Visual Studio 2010\Projects\LambadaSerializeDemo\Liuliu.Wcf.Client\Program.cs:行号 37 InnerException:
而发生异常的场景是这样的,在客户端代码添加了一个输入字符串作为查询条件的一部分(17行):
客户端代码:
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 Console.WriteLine(predicate);22 xmlPredicate = serializer.Serialize(predicate);23 result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(xmlPredicate));24 Console.WriteLine(result.Email);25 }26 }27 catch (Exception e)28 {29 Console.WriteLine(e);30 }31 Console.ReadLine();32 }
这样一个很正常的需求,居然又引出了一个大问题,详情请听下回分解^_^
本文源代码下载:LambadaSerializeDemo02.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 的问题
- AFN框架(二)的使用和有关序列化器的问题--缓存机制------
- Perl语言入门(第五版) 读书笔记(一)---列表、数组、子程序
- Quartz在真实项目中的使用(结合Spring框架)
- 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精品开源项目