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


    在园子里混迹多年,始终保持着“只看帖不回帖”的习惯,看了很多,学了很多,却从不敢写些东西贴出来,一来没什么可写的,二来水平不够,怕误人子弟……
    
    最近在做一个MVC+WCF+EF的项目,遇到问题不少,但大多数问题都是前人遇到并解决了的,感谢园子里的大牛们的无私奉献。
    
    俗话说“礼尚往来”,我也在此分享一个最近在项目中遇到的问题,就是远程调用时的Expression表达式的序列化问题的初始解决方案,希望抛出的这块石头能引出完美的钻石来,同时第一次写博客,请大家多多赐教……
    
    为了说明问题,我将用一个简单的示例来演示,文章的最后会有示例的源代码下载。
    
    示例说明:
    演示项目还是使用传统的四层结构:
    
    
    
    WCF服务契约:契约很简单,一个Member类的Model,一个通过表达式来查找Member信息的GetMember(Expression<Func<Member, bool>>)方法
    
    
     1 using System;
     2 using System.Linq.Expressions;
     3 using System.Runtime.Serialization;
     4 using System.ServiceModel;
     5 
     6 namespace Liuliu.Wcf.IContract
     7 {
     8     [ServiceContract]
     9     public interface IAccountContract
    10     {
    11         [OperationContract]
    12         Member GetMember(Expression<Func<Member, bool>> predicate);
    13     }
    14 
    15     [DataContract]
    16     public class Member
    17     {
    18         [DataMember]
    19         public int MemberID { get; set; }
    20 
    21         [DataMember]
    22         public string UserName { get; set; }
    23 
    24         [DataMember]
    25         public string Email { get; set; }
    26     }
    27 }
    
    为简化客户端的调用操作,在Contracts项目中添加一个WcfHelper辅助类
    
    
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.ServiceModel;
     5 using System.Text;
     6 
     7 namespace Liuliu.Wcf.IContract.Helper
     8 {
     9     public class WcfHelper
    10     {
    11         public static TReturn InvokeService<TContract, TReturn>(Func<TContract, TReturn> func)
    12         {
    13             var channelFactory = new ChannelFactory<TContract>("*");
    14             var proxy = channelFactory.CreateChannel();
    15             var iproxy = proxy as ICommunicationObject;
    16             if (iproxy == null)
    17             {
    18                 throw new ArgumentException("执行远程方法时服务契约代理协议为空。");
    19             }
    20             try
    21             {
    22                 iproxy.Open();
    23                 var result = func(proxy);
    24                 iproxy.Close();
    25                 return result;
    26             }
    27             catch (CommunicationException)
    28             {
    29                 iproxy.Abort();
    30                 throw;
    31             }
    32             catch (TimeoutException)
    33             {
    34                 iproxy.Abort();
    35                 throw;
    36             }
    37             catch (Exception)
    38             {
    39                 iproxy.Close();
    40                 throw;
    41             }
    42         }
    43     }
    44 }
    
    服务的实现也非常简单,就是从数据源DataSource中按条件查找相关信息并返回
    
    
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Linq.Expressions;
     5 using Liuliu.Wcf.IContract;
     6 
     7 namespace Liuliu.Wcf.Services
     8 {
     9     public class AccountService : IAccountContract
    10     {
    11         private readonly static List<Member> DataSource = new List<Member>
    12         {
    13             new Member {MemberID = 3, UserName = "zhangsan", Email = "zhangsan@abc.com"},
    14             new Member {MemberID = 4, UserName = "lisi", Email = "lisi@abc.com"},
    15             new Member {MemberID = 5, UserName = "wangwu", Email = "wangwu@abc.com"},
    16             new Member {MemberID = 6, UserName = "zhaoliu", Email = "zhaoliu@abc.com"}
    17         };
    18 
    19         public Member GetMember(Expression<Func<Member, bool>> predicate)
    20         {
    21             return DataSource.SingleOrDefault(predicate.Compile());
    22         }
    23     }
    24 }
    
    
    
    
    服务端以一个控制台的程序来承载,第一次写博客,可以力求完美一点^_^
    
    
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.ServiceModel;
     5 
     6 using Liuliu.Wcf.Services;
     7 
     8 namespace Liuliu.Wcf.Hosting
     9 {
    10     class Program
    11     {
    12         private static readonly List<ServiceHost> OpenedHosts = new List<ServiceHost>();
    13 
    14         private static void Main(string[] args)
    15         {
    16             try
    17             {
    18                 var accountHost = new ServiceHost(typeof(AccountService));
    19                 var hosts = new List<ServiceHost> { accountHost };
    20                 CreateHosting(hosts);
    21                 OpenHosting(hosts);
    22                 Console.WriteLine("按任意键关闭服务。");
    23                 Console.ReadKey();
    24                 CloseHosting(OpenedHosts);
    25 
    26             }
    27             catch (Exception e)
    28             {
    29                 Console.WriteLine(e);
    30                 Console.ReadLine();
    31             }
    32         }
    33 
    34         private static void OpenHosting(IEnumerable<ServiceHost> hosts)
    35         {
    36             foreach (var host in hosts)
    37             {
    38                 try
    39                 {
    40                     host.Open();
    41                     if (!OpenedHosts.Contains(host))
    42                     {
    43                         OpenedHosts.Add(host);
    44                     }
    45                 }
    46                 catch (Exception)
    47                 {
    48                     foreach (var openedHost in OpenedHosts)
    49                     {
    50                         openedHost.Close();
    51                     }
    52                     throw;
    53                 }
    54             }
    55         }
    56 
    57         private static void CreateHosting(IEnumerable<ServiceHost> hosts)
    58         {
    59             hosts.ToList().ForEach(host =>
    60             {
    61                 host.Opened += host_Opened;
    62                 host.Closed += host_Closed;
    63                 host.UnknownMessageReceived += host_UnknownMessageReceived;
    64             });
    65         }
    66 
    67         private static void CloseHosting(IEnumerable<ServiceHost> hosts)
    68         {
    69             hosts.ToList().ForEach(host => host.Close());
    70         }
    71 
    72         static void host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
    73         {
    74             var host = (ServiceHost)sender;
    75             Console.WriteLine("{0}收到未知消息。", host.Description.ConfigurationName);
    76         }
    77 
    78         static void host_Closed(object sender, EventArgs e)
    79         {
    80             var host = (ServiceHost)sender;
    81             Console.WriteLine("{0}已成功关闭。", host.Description.ConfigurationName);
    82         }
    83 
    84         static void host_Opened(object sender, EventArgs e)
    85         {
    86             var host = (ServiceHost)sender;
    87             Console.WriteLine("{0}服务启动完毕\n监听地址:{1}", host.Description.ConfigurationName, host.Description.Endpoints.First().ListenUri);
    88         }
    89     }
    90 }
    
    当然现在服务端还不能启动,还需要必要的配置信息,在Hosting项目中添加一个App.Config来进行配置,可以通过VS2010中菜单“工具” - “WCF服务配置编辑器” 来进行可视化添加
    
    为了简化配置,这里不进行调用的验证了,设置 clientCredentialType="None"
    
     
    
    
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <system.serviceModel>
            <bindings>
                <netTcpBinding>
                    <binding name="NewBinding0">
                        <security>
                            <message clientCredentialType="None" />
                        </security>
                    </binding>
                </netTcpBinding>
            </bindings>
            <services>
                <service name="Liuliu.Wcf.Services.AccountService">
                    <endpoint address="net.tcp://127.0.0.1:9000/account" binding="netTcpBinding"
                        bindingConfiguration="NewBinding0" contract="Liuliu.Wcf.IContract.IAccountContract" />
                </service>
            </services>
        </system.serviceModel>
    </configuration>
    
     
    
     
    
     
    
    有了前面的WcfHelper的辅助,客户端调用就非常惬意了,由UserName查找Member信息并显示结果的Email信息
    
    
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Linq.Expressions;
     5 using System.Text;
     6 
     7 using Liuliu.Wcf.IContract;
     8 using Liuliu.Wcf.IContract.Helper;
     9 
    10 namespace Liuliu.Wcf.Client
    11 {
    12     class Program
    13     {
    14         static void Main(string[] args)
    15         {
    16             Expression<Func<Member, bool>> predicate = m => m.UserName == "张三";
    17             var result = WcfHelper.InvokeService<IAccountContract, Member>(wcf => wcf.GetMember(predicate));
    18             Console.WriteLine(result.Email);
    19         }
    20     }
    21 }
    
    当然,还要客户端的配置信息,由“WCF服务配置编辑器”工具可直接由上面配置的服务端配置来生成客户端配置
    
    
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <system.serviceModel>
            <bindings>
                <netTcpBinding>
                    <binding name="NewBinding0">
                        <security>
                            <message clientCredentialType="None" />
                        </security>
                    </binding>
                </netTcpBinding>
            </bindings>
            <client>
                <endpoint address="net.tcp://127.0.0.1:9000/account" binding="netTcpBinding"
                    bindingConfiguration="NewBinding0" contract="Liuliu.Wcf.IContract.IAccountContract"
                    name="" kind="" endpointConfiguration="" />
            </client>
        </system.serviceModel>
    </configuration>
    
     
    
    至此,演示项目的基本架构搭建完毕,理论上运行的话应该能顺利启动,右键解决方案 - 设置启动项目,设置启动为多项目启动:
    
    
    
    当我们信心满满的点下启动按键之后,结果却不是我们所期待的,服务端在执行到OpenHosting(hosts)的时候引发了异常:
    
    View Code
     
    
    由异常信息我们可以很明确的看到,System.Linq.Expressions.Expression类不支持序列化操作,以Expression作为查询条件的参数传递是行不通的。
    此路不通,我们只能另辟蹊径了
    
    以“Expression”,“序列化”,“Lambada”等为关键字百度Google了一把,终于还是收获不少,找到了Expression Tree Serializer 这根救命稻草
    
    有路了就要往下走,后面还会遇到什么问题,且听下回分解……
    
     最后,把本文的源代码发上来以供参考,注意:为了保持现场,这个源码包的程序并不能运行
    
    LambadaSerializeDemo01.rar
    
    
    
    
    
    作者:郭明锋
    出处:http://www.cnblogs.com/guomingfeng
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    beanshell-接口返回结果与预期结果比较(预期结果为两个接口差集)-两个set集合的比较
    jmeter断言之Beanshell断言(判断数据库结果是否符合预期)
    beanshell查询结果多条取满足条件的一条数据作为前置步骤给其他接口参数使用
    beanshell判断响应数据是jsonobject类型还是jsonarray类型
    yarn global add安装的目录(window10)
    React 使用axios,将axios注入到全局使用
    解决H5支付宝支付空白页问题
    React Ant Design Mobile ListView 上拉刷新,下拉加载
    vue点击实现复制
    element 设置table表头样式
  • 原文地址:https://www.cnblogs.com/ruishuang208/p/3104106.html
Copyright © 2020-2023  润新知