将实体框架对象图序列化为 Json 时防止 StackOverflowException

发布于 2025-01-02 20:20:38 字数 1182 浏览 4 评论 0原文

我想序列化 实体框架自我跟踪实体完整对象图(一对多关系中的父+子)转换为 Json。

对于序列化,我使用 ServiceStack.JsonSerializer

这就是我的数据库的样子(为简单起见,我删除了所有不相关的字段):

ERD

我在中获取完整的个人资料图这样:

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 遇到堆栈溢出情况?

注意:我不想将我的对象投影为匿名类型(例如这些< a href="https://stackoverflow.com/a/658056/290343">建议),因为这会引入非常长且难以维护的代码片段)。

I want to serialize an Entity Framework Self-Tracking Entities full object graph (parent + children in one to many relationships) into Json.

For serializing I use ServiceStack.JsonSerializer.

This is how my database looks like (for simplicity, I dropped all irrelevant fields):

ERD

I fetch a full profile graph in this way:

public Profile GetUserProfile(Guid userID)
{
    using (var db = new AcmeEntities())
    {
        return db.Profiles.Include("ProfileImages").Single(p => p.UserId == userId);
    }
}

The problem is that attempting to serialize it:

Profile profile = GetUserProfile(userId);
ServiceStack.JsonSerializer.SerializeToString(profile);

produces a StackOverflowException.
I believe that this is because EF provides an infinite model that screws the serializer up. That is, I can techincally call: profile.ProfileImages[0].Profile.ProfileImages[0].Profile ... and so on.

How can I "flatten" my EF object graph or otherwise prevent ServiceStack.JsonSerializer from running into stack overflow situation?

Note: I don't want to project my object into an anonymous type (like these suggestions) because that would introduce a very long and hard-to-maintain fragment of code).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

花心好男孩 2025-01-09 20:20:38

您有相互矛盾的担忧,EF 模型针对将数据模型存储在 RDBMS 中进行了优化,而不是针对序列化 - 这就是单独的 DTO 所扮演的角色。否则,您的客户端将绑定到您的数据库,其中数据模型的每次更改都有可能破坏您现有的服务客户端。

话虽如此,正确的做法是维护单独的 DTO,您映射到的 DTO 定义了您希望模型从外部世界看起来像的所需形状(也称为线格式)。

ServiceStack.Common 包含内置映射函数(即 TranslateTo/PopulateFrom),可简化实体到 DTO 的映射,反之亦然。下面是一个示例:

https://groups.google.com/d/msg/servicestack/ BF-egdVm3M8/0DXLIeDoVJEJ

另一种方法是使用 [DataContract] / [DataMember] 装饰要在数据模型上序列化的字段字段。任何未归属于 [DataMember] 的属性都不会被序列化 - 因此您可以使用它来隐藏导致 StackOverflowException 的循环引用。

You have conflicting concerns, the EF model is optimized for storing your data model in an RDBMS, and not for serialization - which is what role having separate DTOs would play. Otherwise your clients will be binded to your Database where every change on your data model has the potential to break your existing service clients.

With that said, the right thing to do would be to maintain separate DTOs that you map to which defines the desired shape (aka wireformat) that you want the models to look like from the outside world.

ServiceStack.Common includes built-in mapping functions (i.e. TranslateTo/PopulateFrom) that simplifies mapping entities to DTOs and vice-versa. Here's an example showing this:

https://groups.google.com/d/msg/servicestack/BF-egdVm3M8/0DXLIeDoVJEJ

The alternative is to decorate the fields you want to serialize on your Data Model with [DataContract] / [DataMember] fields. Any properties not attributed with [DataMember] wont be serialized - so you would use this to hide the cyclical references which are causing the StackOverflowException.

做个少女永远怀春 2025-01-09 20:20:38

为了解决这个问题的 StackOverflower 同胞,我将解释一下我最终做了什么:

在我描述的情况下,您必须使用标准 .NET 序列化器(而不是 ServiceStack 的): System.Web.Script.Serialization.JavaScriptSerializer。原因是您可以在 [ScriptIgnore] 属性中修饰您不希望序列化程序处理的导航属性。

顺便说一句,您仍然可以使用 ServiceStack.JsonSerializer 进行反序列化 - 它比 .NET 更快,而且您不会遇到我问这个问题的 StackOverflowException 问题。

另一个问题是如何让自跟踪实体用[ScriptIgnore]装饰相关的导航属性。

说明:如果没有[ScriptIgnore],序列化(使用.NET Javascript序列化器)也会引发关于循环的异常
引用(类似于引发 StackOverflowException 的问题
服务堆栈)。我们需要消除循环,这样就完成了
使用[ScriptIgnore]

因此,我编辑了 ADO.NET 自跟踪实体生成器模板 并将其设置为在相关位置包含 [ScriptIgnore] (如果有人想要代码差异,请编写我评论)。有人说编辑这些“外部”的、不应该编辑的文件是一种不好的做法,但是哎呀 - 它解决了问题,并且这是不强迫我重新构建整个系统的唯一方法应用程序(使用 POCO 而不是 STE,对所有内容都使用 DTO 等)

@mythz:我不完全同意您关于使用 DTO 的争论 - 请参阅我对您答案的评论。我非常感谢您为构建 ServiceStack(所有模块!)并使其免费使用和开源所做的巨大努力。我只是鼓励您要么尊重文本序列化程序中的 [ScriptIgnore] 属性,要么提出您自己的属性。否则,即使实际上可以使用 DTO,他们也无法将导航属性从子对象添加回父对象,因为他们会收到 StackOverflowException。
我确实将您的答案标记为“已接受”,因为毕竟它帮助我找到了解决这个问题的方法。

For the sake of my fellow StackOverflowers that get into this question, I'll explain what I eventually did:

In the case I described, you have to use the standard .NET serializer (rather than ServiceStack's): System.Web.Script.Serialization.JavaScriptSerializer. The reason is that you can decorate navigation properties you don't want the serializer to handle in a [ScriptIgnore] attribute.

By the way, you can still use ServiceStack.JsonSerializer for deserializing - it's faster than .NET's and you don't have the StackOverflowException issues I asked this question about.

The other problem is how to get the Self-Tracking Entities to decorate relevant navigation properties with [ScriptIgnore].

Explanation: Without [ScriptIgnore], serializing (using .NET Javascript serializer) will also raise an exception, about circular
references (similar to the issue that raises StackOverflowException in
ServiceStack). We need to eliminate the circularity, and this is done
using [ScriptIgnore].

So I edited the .TT file that came with ADO.NET Self-Tracking Entity Generator Template and set it to contain [ScriptIgnore] in relevant places (if someone will want the code diff, write me a comment). Some say that it's a bad practice to edit these "external", not-meant-to-be-edited files, but heck - it solves the problem, and it's the only way that doesn't force me to re-architect my whole application (use POCOs instead of STEs, use DTOs for everything etc.)

@mythz: I don't absolutely agree with your argue about using DTOs - see me comments to your answer. I really appreciate your enormous efforts building ServiceStack (all of the modules!) and making it free to use and open-source. I just encourage you to either respect [ScriptIgnore] attribute in your text serializers or come up with an attribute of yours. Else, even if one actually can use DTOs, they can't add navigation properties from a child object back to a parent one because they'll get a StackOverflowException.
I do mark your answer as "accepted" because after all, it helped me finding my way in this issue.

难理解 2025-01-09 20:20:38

确保在序列化实体之前将其与 ObjectContext 分离。

我还使用了牛顿 JsonSerializer。

JsonConvert.SerializeObject(EntityObject, Formatting.Indented, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });

Be sure to Detach entity from ObjectContext before Serializing it.

I also used Newton JsonSerializer.

JsonConvert.SerializeObject(EntityObject, Formatting.Indented, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文