JSON を介して WCF Web サービスを介してデータ モデル (コード ファースト エンティティ フレームワーク) を公開しようとしています。モデルには複数の多対多の関係があり、遅延読み込みが有効になっています。私たちの Web サービスは、複数の子オブジェクトの最初のレベルのみを返すことができるはずです。次のエラーが引き続き表示されます。
「現在のスレッドがスタック オーバーフロー状態にあるため、式を評価できません。」
このエラーは、循環参照を持つデータをシリアル化するときに発生することを認識しています。
別のスレッドを読んだ後、循環参照を処理するために次の解決策を試みました。
- WCF および DataContractSerializer: エンティティを DataContract[IsReference=true] で明示的にマークし、すべてのプロパティを [DataMember] 属性でマークします。これにより、循環参照を使用できるようになります。T4 テンプレートを使用してエンティティを生成している場合は、テンプレートを変更してこれらの属性を追加する必要があります。
- WCF と DataContractSerializer: 暗黙的なシリアル化。関連するナビゲーション プロパティの 1 つを [IgnoreDataMember] 属性でマークして、プロパティがシリアル化されないようにします。
- XmlSerializer: 関連するナビゲーション プロパティを [XmlIgnore] 属性でマークする
- その他のシリアライゼーション: 関連するナビゲーション プロパティの 1 つを [NonSerialized] (Haz が最初に言及したのは +1) でマークし、一般的なシリアライゼーションまたは [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 で多対多の関係を持つオブジェクトのシリアル化を処理するための堅実なアプローチを探しています。私は、データ モデルに変更を加えたり、何らかの方法で 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();
}