• 【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
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    论语学习系列(一)
    如何编译生成 dll
    Lua学习系列(二)
    Lua学习系列(一)
    Promise对象
    ... 扩展运算符
    class类
    proxy [ˈprɒksi] 代理
    HBuilder 打包流程
    Generator [ˈdʒenəreɪtə(r)] 函数结构
  • 原文地址:https://www.cnblogs.com/ruishuang208/p/3104106.html
Copyright © 2020-2023  润新知