首页 文章

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

提问于
浏览
2

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

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

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

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

  • WCF和DataContractSerializer:使用DataContract [IsReference = true]显式标记您的实体,使用[DataMember]属性显式标记所有属性 . 这将允许您使用循环引用 . 如果您使用T4模板生成实体,则必须对其进行修改以为您添加这些属性 .

  • WCF和DataContractSerializer:隐式序列化 . 使用[IgnoreDataMember]属性标记其中一个相关导航属性,以便不对该属性进行序列化 .

  • XmlSerializer:使用[XmlIgnore]属性标记一个相关的导航属性

  • 其他序列化:使用[NonSerialized]标记其中一个相关的导航属性(对于Haz,他是第一个提到这个的),用于常见序列化或[ScriptIgnore]用于某些与JSON相关的序列化 .

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

[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中具有多对多关系的对象的序列化-OR-只是为了打破第一级子代下面的引用 . 我愿意以某种方式更改数据模型或重新配置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();
}

2 回答

  • 0

    如果只需要单个级别,则必须关闭延迟加载并使用预先加载来加载第一级子级实体 . 如果存在此类导航属性,这可能仍会导致从子级到父级的一些向后序列化 . 要完全避免所有序列化问题,必须使用DTO(数据传输对象),它只模拟您要使用的数据和单向关系 . 您将在服务操作中填写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; }
    }
    

相关问题