我想将一个Entity Framework Self-Tracking Entities完整对象图(一对多关系中的父+子)序列化为Json。
对于序列化,我使用 ServiceStack.JsonSerializer 。
这就是我的数据库的样子(为简单起见,我删除了所有不相关的字段):
我以这种方式获取完整的个人资料图表:
public Profile GetUserProfile(Guid userID)
{
using (var db = new AcmeEntities())
{
return db.Profiles.Include("ProfileImages").Single(p => p.UserId == userId);
}
}
问题在于尝试序列化它:
Profile profile = GetUserProfile(userId);
ServiceStack.JsonSerializer.SerializeToString(profile);
生成StackOverflowException
。
我相信这是因为EF提供了一个无限模型,可以将串行器拧紧。也就是说,我可以称之为:profile.ProfileImages[0].Profile.ProfileImages[0].Profile ...
等等。
如何“压扁”我的EF对象图或以其他方式阻止 ServiceStack.JsonSerializer 进入堆栈溢出情况?
注意:我不想将我的对象投射到匿名类型(如these suggestions),因为这会引入一个非常长且难以 - 维护代码片段。)
答案 0 :(得分:9)
您有相互矛盾的问题,EF模型已经过优化,可以将您的数据模型存储在RDBMS中,而不是用于序列化 - 这是具有单独DTO的角色。否则,您的客户将绑定到您的数据库,您的数据模型上的每个更改都有可能破坏您现有的服务客户端。
话虽如此,正确的做法是维护您映射到的单独DTO,这些DTO定义了您希望模型从外部世界看起来的所需形状(也称为线格式)。
ServiceStack.Common包括内置的映射函数(即TranslateTo / PopulateFrom),它简化了将实体映射到DTO,反之亦然。这是一个显示这个的例子:
https://groups.google.com/d/msg/servicestack/BF-egdVm3M8/0DXLIeDoVJEJ
另一种方法是使用[DataContract] / [DataMember]字段装饰要在数据模型上序列化的字段。任何未归属于[DataMember]的属性都不会被序列化 - 因此您可以使用它来隐藏导致StackOverflowException的循环引用。
答案 1 :(得分:7)
为了我的同伴StackOverflowers进入这个问题,我将解释我最终做了什么:
在我描述的情况下,您必须使用标准的.NET序列化程序(而不是ServiceStack):System.Web.Script.Serialization.JavaScriptSerializer
。原因是您可以装饰您不希望序列化程序在[ScriptIgnore]
属性中处理的导航属性。
顺便说一下,您仍然可以使用ServiceStack.JsonSerializer
进行反序列化 - 它比.NET更快,而且您没有问过StackOverflowException问题。
另一个问题是如何让自我跟踪实体用[ScriptIgnore]
修饰相关的导航属性。
说明:如果没有
[ScriptIgnore]
,序列化(使用.NET Javascript序列化程序)也会引发异常,关于循环 引用(类似于引发StackOverflowException的问题 ServiceStack)。我们需要消除循环,这就完成了 使用[ScriptIgnore]
。
因此,我编辑了ADO.NET Self-Tracking Entity Generator Template附带的.TT
文件,并将其设置为在相关位置包含[ScriptIgnore]
(如果有人想要代码差异,请写一条评论)。有人说编辑这些“外部”,不是意味着要编辑的文件是一种不好的做法,但是哎呀 - 它解决了这个问题,这是唯一不会迫使我重新构建整个问题的方法。应用(使用POCO而不是STE,将DTO用于所有等等。)
答案 2 :(得分:1)
确保在序列化之前从ObjectContext中分离实体。
我还使用了Newton JsonSerializer。
JsonConvert.SerializeObject(EntityObject,Formatting.Indented,new JsonSerializerSettings {PreserveReferencesHandling = PreserveReferencesHandling.Objects});