zoukankan      html  css  js  c++  java
  • 转载:用WCF + Entity Framework 序列化时的陷阱

    转载自:http://www.cnblogs.com/xiaomi7732/archive/2011/07/30/2120626.html

    当使用WCF + Entity Framework时要小心,否则,很容易掉入各种陷阱。这里介绍两个在序列化时容易遇到的、会导致服务停止的陷阱。

    一、试图序列化Entity Proxy类而导致服务停止。

    二、序列化时出现死循环导致服务停止;

    无论掉入哪一个陷阱,在客户端都会看到这样的对话框(点击看大图):

    24805507_3

    大致意思是会说:服务不在线或者客户端配置有问题或者Proxy类有问题。而如果跟着Error Details里的第一行Google,会找到许多文章关于调整数据缓存大小、调整操作超时时间……如果按照常规则调试方法——按图索骥找问题,那么就可能越陷越深。

    查找问题时,首先要透过假象,看到问题的本质——WCF服务因为某些原因而停止了。其次,我们要具体分析问题的特殊性,结合经验,来查找问题。

    服务停止有一个典型的原因,那就是序列化出问题了。由于在WCF中,序列化不需要我们写代码实现,它在为我们提供便利的同时,也埋下了伏笔。而EF的使用,又进一步将事情变得错综复杂。凡事有弊有利,往往是使用了EF这样的特殊性,让我们查找问题时也更具方向性。

    EF中,有什么特性会与WCF交互?没错,我们常常把EF的实体类用作DataContract。EF的实体类,无论是生成的还是通过Code First写的,运行时,为了支持EF的一些“高级”功能,例如,LazyLoad,Runtime会从你的Entity类自动继承出一个对应的代理类(Proxy Types)并对代理类对象进行操作。例如Person类,在运行时或者就会对应有一个叫Person123456类(肯定不会叫这个名字啦,这权当标识一下)。我们写代码时对Person的操作,运行时会换成对Person123456的操作。

    通过调试窗口,我们可以到看到这个隐藏在背后的类(代码类为Handbook,实际类为DynamicProxies.Handbook_AE28…84):

    24805507_4

    这样一来,WCF,具体的说,是DataContractSerializer就不认账了——我只认识老子(Handbook),不认识儿子(代理类),没法序列化!于是它抛出了异常,导致服务中断。这样一来,我们会看到,用WCF返回一般的对象没有问题,一旦返回实体类对象,就会遇到了文章开始时看到的错误。

    一个简单的解决方法:让DbContext使用静态类。在对应的DbContext的构造函数里添加一个设置即可:

       1:  public partial class MTBContainer : DbContext
       2:      {
       3:          public MTBContainer()
       4:              : base("name=MTBContainer")
       5:          {
       6:              this.Configuration.ProxyCreationEnabled = false; // This is the line added.
       7:          }
       8:          //...
       9:          public IDbSet<PlacePictureType> PlacePictureTypes { get; set; }
      10:          //...
      11:      }

    第二个容易遇到的问题是序列化死循环的问题。乍一看错误跟第一个显示的是一样的:服务不可用了!而且同样是序列化问题。

    但是,这类问题有一个特别的地方:有些实体类对象能正常返回,有些不能。

    经过观察分析发现,不能返回的类往往是那些有Navigation属性的实体类,进一步分析,Navigation属性往往会构成对自身的间接引用: 例如在一对多表里,往往一端有一个多端对象的集合,同时,多端又有一个一端的引用。比如有一个Parent实体,有一个Child实体,那 么,Parent里会有一个ICollection<Child>用来存储所有的孩子,而Child里又会有一个Parent用来指明当前这 个Child的Parent。这种互相引用的关系导致了DataContractSerializer在序列化它时出现了问题。

    看来,不光光是在EF中会遇到这样的问题,DataContractSerializer序列化一个直接或者间接引用了自己的类对象,就会陷入死循环。

    说话……Visual Studio的图标是不是像一个死循环?^v^

    24805507_5

    回正题,以上面描述的类为例:

       1:      //[DataContract(IsReference = true)]
       2:      [DataContract]
       3:      public class Parent
       4:      {
       5:          public Parent()
       6:          {
       7:              Children = new List<Child>();
       8:          }
       9:          [DataMember]
      10:          public string Name { get; set; }
      11:          [DataMember]
      12:          public IList<Child> Children { get; set; }
      13:      }
      14:  
      15:      //[DataContract(IsReference = true)]
      16:      [DataContract]
      17:      public class Child
      18:      {
      19:          [DataMember]
      20:          public string Name { get; set; }
      21:          [DataMember]
      22:          public Parent ParentRef { get; set; }
      23:      }

    我们在Console Applicatin的Main函数里试着用DataContractSerializer序列化它:

       1:              // Create mock objects
       2:              Parent p = new Parent() { Name = "Baba" };
       3:              Child c1 = new Child() { Name = "John", ParentRef = p };
       4:              Child c2 = new Child() { Name = "Alice", ParentRef = p };
       5:              p.Children.Add(c1);
       6:              p.Children.Add(c2);
       7:             
       8:             // Creat DataContractSerializer
       9:              DataContractSerializer serializer = new DataContractSerializer(typeof(Parent));
      10:             
      11:              // Serialize & output results.
      12:              string result = null;
      13:              using (Stream s = new MemoryStream())
      14:              {
      15:                  serializer.WriteObject(s, p);
      16:                  s.Seek(0, SeekOrigin.Begin);
      17:  
      18:                  using (StreamReader r = new StreamReader(s))
      19:                  {
      20:                      result = r.ReadToEnd();
      21:                  }
      22:              }
      23:              Console.WriteLine(result);

    运行结果是:Crash。默认情况下,DataContractSerializer是这么把对象层层展开

       1:  <Parent>
       2:     <Children>
       3:         <Child>
       4:             <Parent>    <!—- this is Child.Parent Property… Enter into endless loop -->
       5:                 <Children>

    它不会顾及引用关系,只会一味的把对象的属性展成对应的XML元素。因此,Parent –> Child1 –> Parent –> Child1…无穷匮也了。

    解决方法一、已经在上面的代码里了:在DataContract上使用IsReference参数,并且设置为true。([DataContract(IsReference = true)])。

    解决方法二、调用DataContractSerializer的另一个构造函数来替代上面代码中的第9行:

       1:  DataContractSerializer serializer = new DataContractSerializer(typeof(Parent),
       2:          "Parent",
       3:          string.Empty,
       4:          null,
       5:          int.MaxValue,
       6:          false,
       7:          true,
       8:          null,
       9:          null);

    第7行用来表明使用Preserve Object Reference。使用了Preserve Object Reference以后,会产生类似以下代码的序列化结果,每个元素都会有其对应的Id,对象引用对引用Id

       1:  <Parent z:Id="i1" xmlns="http://schemas.datacontract.org/2004/07/Sealize" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
       2:      <Children>
       3:          <Child z:Id="i2">
       4:          <Name>John</Name>
       5:          <ParentRef z:Ref="i1"/>
       6:          </Child>
       7:          <Child z:Id="i3">
       8:              <Name>Alice</Name>
       9:              <ParentRef z:Ref="i1"/>
      10:          </Child>
      11:      </Children>
      12:      <Name>Baba</Name>
      13:  </Parent>

    WCF的异常信息不准确给问题判断带来了很大的难度。但是,看清问题本质,分析问题的特殊性,加之一定经验的积累,就能很快的找准问题并解决之。

    假像总是会露出破绽的。例如,虽然异常信息提示可能是超时,但如果一调用服务马上返回错误,一般就不会是超时问题。如果错误提示说可能数据超过允许返回的大小,那么,试着返回一个单一个对象而非集合……

    当然,我们还是在不断努力,希望给大家带来提供更加易用的Visual Studio。

  • 相关阅读:
    解决SharePoint 文档库itemadded eventhandler导致的上传完成后,编辑页面保持报错的问题,错误信息为“该文档已经被编辑过 the file has been modified by...”
    解决SharePoint 2013 designer workflow 在发布的报错“负载平衡没有设置”The workflow files were saved but cannot be run.
    随机实例,随机值
    Spring4笔记
    struts2笔记(3)
    struts2笔记(2)
    获取文本的编码类型(from logparse)
    FileUtil(from logparser)
    DateUtil(SimpleDateFormat)
    struts2笔记
  • 原文地址:https://www.cnblogs.com/whx1973/p/3075248.html
Copyright © 2011-2022 走看看