zoukankan      html  css  js  c++  java
  • 【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

    如果您看完本篇文章感觉不错,请点击一下右下角的推荐来支持一下博主,谢谢!

    作者:郭明锋

    Q群:MVC EF技术交流(5008599)MVC EF 技术交流 OSharp开发框架交流(85895249)OSharp开发框架交流

    出处https://www.cnblogs.com/guomingfeng

    声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


  • 相关阅读:
    玩转渗透神器Kali:Kali Linux作为主系统使用的正确姿势TIPS
    知道创宇研发技能表v2.2
    我对什么都感兴趣,可我迷茫了(转载)
    防御性编程
    防御性编程技巧
    移动安全技术如何未雨绸缪?
    1054. 求平均值 (20)
    1053. 住房空置率 (20)
    1052. 卖个萌 (20)
    1051. 复数乘法 (15)
  • 原文地址:https://www.cnblogs.com/guomingfeng/p/2439858.html
Copyright © 2011-2022 走看看