【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”

我们再次信心满满的修改多项目启动,运行……服务端与客户端都如愿正常启动了:

 

但是,客户端按任意键执行调用的时候……客户端发生了如下异常:

 

View Code
捕捉到 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 }
复制代码

 

再次运行,报如下异常:

View Code
捕捉到 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

客户端发送的XElement数据
<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>

 

服务端接收的XElement数据
<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序列化的问题了,可是……某日突然报了这么个异常:

 

View Code
捕捉到 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

0 0
原创粉丝点击