获取JsonSerializationException

时间:2018-01-17 18:38:44

标签: entity-framework exception asp.net-web-api

我在尝试将对象转换为json时遇到问题。错误是Newtonsoft.Json.JsonSerializationException:

为属性' Project'检测到自引用循环。类型' System.Data.Entity.DynamicProxies.Project_F29F70EF89942F6344C5B0A3A7910EF55268857CD0ECC4A484776B2F4394EF79'。路径' [0] .Categories [0]'。

问题是对象(它实际上是一个对象列表)有一个属性,它是另一个引用回第一个对象的对象:

public partial class Project
{
...
    public virtual ICollection<Category> Categories { get; set; }
...
}

public partial class Category
{
...
    public virtual Project Project { get; set; }
...
}

就实体框架而言,这一切都很好,但是将其转换为json会导致无限回归,因此也是例外。

这是我的代码:

    public async Task<HttpResponseMessage> GetProjects()
    {
        var projects = _projectService.GetProjects().ToList();
        string jsonString = JsonConvert.SerializeObject(projects); // <-- Offending line
        return Request.CreateResponse(HttpStatusCode.OK, jsonString);
    }

我已经在网上寻找解决方案,我找到了这个stackoverflow帖子:

JSON.NET Error Self referencing loop detected for type

他们提出了三种解决方案,但都不起作用:

1)忽略循环引用:

    public async Task<HttpResponseMessage> GetProjects()
    {
        var projects = _projectService.GetProjects().ToList();
        JsonSerializerSettings settings = new JsonSerializerSettings()
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
        };
        string jsonString = JsonConvert.SerializeObject(projects, settings);
        return Request.CreateResponse(HttpStatusCode.OK, jsonString);
    }

这导致对SerializeObject(...)的调用挂起一点然后抛出一个System.OutOfMemoryException(它告诉我循环引用没有被忽略)。

请注意,这个在stackoverflow上提出的解决方案的作者说要在WebApiConfig.cs中设置忽略设置,但我尝试了它并且没有效果。

他还说:

&#34;如果要在非api ASP.NET项目中使用此修补程序,可以将以上行添加到Global.asax.cs,但首先添加:var config = GlobalConfiguration.Configuration;&# 34;

我的网络API没有全局文件,所以我不应该这样做。

我也不想忽略循环引用,因为我不想丢失数据。

2)保留循环参考:

    public async Task<HttpResponseMessage> GetProjects()
    {
        var projects = _projectService.GetProjects().ToList();
        JsonSerializerSettings settings = new JsonSerializerSettings()
        {
            ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
            PreserveReferencesHandling = PreserveReferencesHandling.Objects
        };
        string jsonString = JsonConvert.SerializeObject(projects, settings);
        return Request.CreateResponse(HttpStatusCode.OK, jsonString);
    }

这只会导致请求超时,因为它会挂起。

同样,作者说要把它放在WebApiConfig.cs中,但这又没有效果。

3)将ignore / preserve引用属性添加到对象和属性中:

忽略类别:

public partial class Project
{
...
    [JsonIgnore]
    public virtual ICollection<Category> Categories { get; set; }
...
}

这没有效果。我将鼠标悬停在项目列表上,看到它仍然有类别,每个类别仍然有一个项目实例。我仍然得到同样的例外。

同样,即使这有效,我也不想忽略这些类别。

保留类别:

[JsonObject(IsReference = true)]
public partial class Project
{
...
    public virtual ICollection<Category> Categories { get; set; }
...
}

同样,结果相同。

即使此方法有效,也不会保留属性。我将在每次重新编译时自动重新生成的Entity Framework类中执行此操作。 (有没有办法告诉它在模型中设置这些属性?我可以在部分类的另一半上设置它们吗?)

或者,除了转换为json并在回复中发送回来之外,我还要接受其他建议。还有另一种方法可以将数据恢复到客户端吗?

这个问题会解决什么问题?感谢。

2 个答案:

答案 0 :(得分:1)

简言之

解决此问题的最佳方法是在Presentation层上创建完全全新的模型(xxxModel,xxxViewModel,xxxResponse等),这些模型将返回给最终用户。不只是使用AutoMapper或您自己的自定义方法将一个对象强制转换为另一个对象。

让您的数据库实体与现实世界分开!

详细

你会遇到很多问题:

  1. 披露敏感数据。您的数据库实体可能/将包含最终用户不应接收的敏感数据;
  2. 性能问题以及RAM和CPU的浪费。最好只加载那些需要最终用户的属性,而不是全部;
  3. 序列化问题。 EF实体几乎总是包含导航属性,如果启用了延迟加载,它们将被序列化在一起。想象一下几十个相关的实体,当你的复合根被序列化时,它们将被延迟加载。它会对数据库造成数十个意外请求;
  4. 脆弱性。与您的EF实体相关的任何更改都将影响表示层和最终用户。例如,在API的情况下,新添加的属性只是扩展响应,但删除或重命名将破坏客户应用程序中的逻辑。
  5. 还有很多其他问题,请小心。

答案 1 :(得分:0)

我建议不要序列化实体框架类并创建一个只继承自Object并且只有你需要的数据的特定类