【Expression 序列化】WCF的简单使用及其Expression Lambada的序列化问题初步解决方案(二)...

 

接上文 【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 IAccountContract
3 {
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

转载于:https://www.cnblogs.com/guomingfeng/archive/2012/04/10/2439858.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值