如何处理循环引用 - 或 - 在 WCF 服务中返回的第一级子项下的引用暴露实体框架数据模型?

Posted

技术标签:

【中文标题】如何处理循环引用 - 或 - 在 WCF 服务中返回的第一级子项下的引用暴露实体框架数据模型?【英文标题】:How to Handle Circular References -OR- break references beneath first level children returned in WCF services exposing Entity Framework data model? 【发布时间】:2012-09-24 21:17:30 【问题描述】:

我正在尝试通过 JSON 上的 WCF Web 服务公开我的数据模型(代码优先实体框架)。该模型具有多个多对多关系并启用了延迟加载。我们的 Web 服务应该能够只返回多个子对象的第一级。我继续收到以下错误:

“无法计算表达式,因为当前线程处于堆栈溢出状态。”

我意识到在序列化具有循环引用的数据时会发生此错误。

在阅读了另一个线程后,我尝试了以下补救措施来处理循环引用:

WCF 和 DataContractSerializer:显式标记您的实体 DataContract[IsReference=true] 和所有带有 [DataMember] 的属性 属性。这将允许您使用循环引用。如果你 正在使用 T4 模板生成实体,您必须修改它才能添加 为您提供这些属性。 WCF 和 DataContractSerializer:隐式序列化。标记其中之一 与 [IgnoreDataMember] 属性相关的导航属性,所以 该属性未序列化。 XmlSerializer:用 [XmlIgnore] 属性 其他序列化:将相关导航属性之一标记为 [NonSerialized](Haz 他是第一个提到这个的 +1) 一些 JSON 相关的通用序列化或 [ScriptIgnore] 序列化。

这些不同的方法都没有奏效。我真正需要的只是子对象的第一级。我不需要孩子的孩子这么说。我添加了以下代码来手动中断对一级子对象的子对象的引用,这有效但不是有效的解决方案:

    [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetReportTypes")]
    public List<ReportType> GetReportTypes()
    
        List<ReportType> result = BusinessLogic.GetReportTypes(_context).ToList();
        foreach (var x in result)
        
            foreach (var y in x.Sections)
            
                y.ReportType = null;
            

        ;
        return result;
    

我正在寻找一种可靠的方法来处理通过 WCF 公开的 EF 中具有多对多关系的对象的序列化 - 或者 - 只是为了打破第一级子级以下的引用。我愿意以某种方式更改数据模型或重新配置 Web 服务。

为了完整起见,下面列出了我所有的相关代码:

[DataContract(IsReference = true)]
[Table("ReportType", Schema = "Reporting")]
public class ReportType

    [Key]
    [Column("ID")]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [DataMember]
    public int Id  get; set; 

    [DataMember]
    public virtual ICollection<ReportTypeSection> Sections  get; set; 



[Table("Section", Schema = "Reporting")]
[DataContract(IsReference = true)]
public class Section

    [Key]
    [Column("ID")]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [DataMember]
    public int Id  get; set; 

    [DataMember]
    public virtual ICollection<ReportTypeSection> ReportTypes  get; set; 


[Table("ReportTypeSection", Schema = "Reporting")]
[DataContract(IsReference=true)]
public class ReportTypeSection

    [Column("ID")]
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [DataMember]
    public int Id  get; set; 

    [Column("ReportTypeID")]
    [Required(ErrorMessage = "Report Type Section Foreign Key Report Type ID is Required")]
    [DataMember]
    public int ReportTypeId  get; set; 

    [Column("SectionID")]
    [Required(ErrorMessage = "Report Type Section Foreign Key Section ID is Required")]
    [DataMember]
    public int SectionId  get; set; 

    [DataMember]
    public virtual ReportType ReportType  get; set; 

    [DataMember]
    public virtual Section Section  get; set; 



public class BusinessLogic

    public static IEnumerable<ReportType> GetReportTypes(IDatabaseContext context)
    
        IEnumerable<ReportType> result = context.ReportTypes
            .Include(a => a.Sections)
            .AsEnumerable();
        return result;
    


public class ReportConfigurationService : IReportConfigurationService

    private IEmspeedDatabaseContext _context;
    public ReportConfigurationService(IEmspeedDatabaseContext context)
    
        _context = context;
    

    [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetReportTypes")]
    public List<ReportType> GetReportTypes()
    
        List<ReportType> result = BusinessLogic.GetReportTypes(_context).ToList();
        return result;
    


[ServiceContract]
public interface IReportConfigurationService


    [OperationContract]
    [ApplyDataContractResolver]
    List<ReportType> GetReportTypes();

【问题讨论】:

【参考方案1】:

如果您只需要单级,则必须关闭延迟加载并使用急切加载来仅加载第一级子实体。如果存在此类导航属性,这仍可能导致从子级到父级的一些反向序列化。为了完全避免所有序列化问题,您必须使用 DTO(数据传输对象),它只对您想要使用的数据和单向关系进行建模。您将在服务操作中填充 DTO 并将其返回 => 通过构建独立于实体模型的 DTO,您将完全控制序列化。

【讨论】:

【参考方案2】:

只是避免将 [DataMember] 属性放在具有 ciclic 引用的属性上(在这种情况下,我使用热切加载):

[DataContract]
public class Persona

    [DataMember]
    [Key]
    public int IdPersona  get; set; 
    [DataMember]
    public string Nombre  get; set; 
    [DataMember]
    public List<Domicilio> Domicilios  get; set; 


[DataContract]
public class Domicilio

    [DataMember]
    [Key]
    public int IdDomicilio  get; set; 
    [DataMember]

    public Persona persona  get; set; 

【讨论】:

以上是关于如何处理循环引用 - 或 - 在 WCF 服务中返回的第一级子项下的引用暴露实体框架数据模型?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 WCF 的 MSMQ 绑定中的消息失败

使用 WCF 和 DefaultCredentials 时如何处理密码更改?

使用 KissXML 处理 wcf Rest Web 服务 - 我应该如何处理命名空间问题

Spring如何处理循环引用

我应该如何处理 Window Phone / WCF / ASP.NET MVC 应用程序的用户身份?

如何处理 SilverLight 4 和 WPF 应用程序的 WCF 身份验证