使用Json.Net序列化Entity Framework对象

8
我该如何将Entity Framework对象序列化为JavaScript对象(JSON)?我尝试使用JSON.NET,但在序列化时出现以下异常。

异常:Newtonsoft.Json.JsonSerializationException,消息=“自引用循环”

Hitesh

3个回答

7
听起来你遇到了与原始DataContract序列化器相同的一般问题,涉及循环引用。虽然对象之间的引用在内存中的对象图中是相当常见的,但如果序列化程序没有专门考虑这种循环引用,这些循环引用不可避免地会导致无限递归。在常见的非二进制序列化格式(XML和JSON是最常见的两种)中,几乎没有任何已建立的标准来处理循环引用。
Microsoft在.NET 3.5 SP1中通过利用xml中的ref语义解决了DataContract序列化器的循环问题。据我所知,JSON中没有这样的东西,这可能就是为什么JSON.NET阻止您对对象图进行序列化的原因。
我会确保您的对象图中只有单向可导航的引用,而不是双向引用(即只从父级到子级,而不是从子级到父级)。这些父/子和子/父是最常见的循环引用类型。还可能是较低级别的子对象最终引用了图的根,从而创建了一个间接的循环图(然而,这些循环图比父/子循环要少得多)。
一旦消除了对象图中的任何循环引用,您就应该能够进行序列化。

谢谢你的回答。我不想在对象图中消除任何循环引用,因为我不完全确定如何做以及进行此更改的影响。相反,我创建了简单的纯对象,并使用Entity Framework对象填充这些对象,然后对这些纯对象进行序列化,它可以正常工作。 - Hitesh
4
通常来说,将领域实体和数据传输对象(DTO)彼此独立是一种好的做法。原生的实体以其丰富、图形化的状态,是解决业务问题并建模业务问题领域的有效方式,但它们不太适合面向服务和序列化。我个人倾向于尽可能保持我的领域实体丰富和相互关联,以便它能够更好地建模业务,并提供独立的服务API,以提供单独可序列化的DTO。 - jrista
这个问题不仅仅是由双向导航属性引起的引用循环的问题那么简单。如果你有10个对象都引用了同一个依赖对象,那该怎么序列化这个图形而不会重复引用对象10次呢?同样,在客户端正确地反序列化数据时,如何重新创建被引用对象的单个实例,而不是10个不同的实例?有没有使用JSON的标准或常见模式来解决这个问题? - Marchy

4

我曾经遇到过这个问题,通过在导致循环的属性上添加Newtonsoft.Json.JsonIgnoreAttribute来解决了它。显然,该属性将不会被序列化。为了解决这个问题,我通常会在实体中同时保存外键引用ID和外键类。我意识到这不是一种直观(或非常好的面向对象)的方式,但这是Julia Lerman在她的书《Programming Entity Framework: Code First》中推荐的方法。我发现它可以帮助解决Entity Framework中遇到的几个问题。

 public class SomeEntity
 {
      [JsonIgnore]
      public ForeignEntity SomeForeignEntity {get;set;}
      public Guid ForeignEntityId {get;set;}
 }

更新:我忘了提到我还需要禁用DbContext上的代理,就像这样:
dataContext.Configuration.ProxyCreationEnabled = false;

如果您正在编写服务端代码(如果您正在序列化,则似乎是这种情况),那么禁用代理创建可能不是问题,但是您会失去一些东西。有关更多详细信息,请参见此处:http://www.sellsbrothers.com/posts/Details/12665
我正在使用MS Web Api,因此在构建控制器时只需禁用代理创建即可:
public class MailingApiController : ApiController
{
    public MailingApiController()
    {
        PreventDeepSerialization();
    }

    private static void PreventDeepSerialization()
    {
        var dataContext = Injector.Get<IIntertwyneDbContext>();
        dataContext.Configuration.ProxyCreationEnabled = false;
    }
      ....

1
为了解决这个问题,我将我的实体转换为基于 POCO 的 Code First。要做到这一点,请在 edmx 窗口内右键单击并选择:
添加代码生成项 > 代码选项卡 > EF POCO 实体生成器。
请注意,如果您没有看到它,您可能需要使用 nuget 安装它。
然而,在运行时,EF 会为这些对象添加代理类以进行跟踪,但它们往往会干扰序列化过程。为了防止这种情况,我们可以将 ProxyCreationEnabled 设置为 false,如下所示:
var context = new YourEntities();
context.Configuration.ProxyCreationEnabled = false;

var results = context.YourEntity.Take(100).ToList();

您可以通过省略默认的引用循环,安全地返回JSON.NET序列化数据,方法如下:

return JsonConvert.SerializeObject(results, Formatting.Indented, 
    new jsonSerializerSettings { 
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore 
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接