检测到自引用循环 - 从WebApi返回数据到浏览器

78 c# serialization entity-framework json.net asp.net-web-api

我正在使用Entity Framework并且在向浏览器中获取父数据和子数据时遇到问题.这是我的课程:

 public class Question
 {
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public virtual ICollection<Answer> Answers { get; set; }
}

public class Answer
{
    public int AnswerId { get; set; }
    public string Text { get; set; }
    public int QuestionId { get; set; }
    public virtual Question Question { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我使用以下代码返回问题和答案数据:

    public IList<Question> GetQuestions(int subTopicId, int questionStatusId)
    {
        var questions = _questionsRepository.GetAll()
            .Where(a => a.SubTopicId == subTopicId &&
                   (questionStatusId == 99 ||
                    a.QuestionStatusId == questionStatusId))
            .Include(a => a.Answers)
            .ToList();
        return questions; 
    }
Run Code Online (Sandbox Code Playgroud)

在C#方面,这似乎有效,但我注意到答案对象有回到问题的参考.当我使用WebAPI将数据提供给浏览器时,我收到以下消息:

'ObjectContent`1'类型无法序列化内容类型'application/json的响应主体; 字符集= UTF-8' .

使用类型"Models.Core.Question"检测属性"问题"的自引用循环.

这是因为问题有答案,而答案有回复问题的参考?我看过的所有地方都建议引用孩子的父母,所以我不知道该怎么办.有人可以给我一些建议.

Sam*_*ach 70

这是因为问题有答案,而答案有回复问题的参考?

是.它无法序列化.

编辑:请参阅Tallmaris的答案和OttO的评论,因为它更简单,可以全局设置.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re??ferenceLoopHandling = ReferenceLoopHandling.Ignore;
Run Code Online (Sandbox Code Playgroud)

旧答案:

将EF对象Question投影到您自己的中间或DataTransferObject.然后可以成功序列化此Dto.

public class QuestionDto
{
    public QuestionDto()
    {
        this.Answers = new List<Answer>();
    } 
    public int QuestionId { get; set; }
    ...
    ...
    public string Title { get; set; }
    public List<Answer> Answers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

就像是:

public IList<QuestionDto> GetQuestions(int subTopicId, int questionStatusId)
{
    var questions = _questionsRepository.GetAll()
        .Where(a => a.SubTopicId == subTopicId &&
               (questionStatusId == 99 ||
                a.QuestionStatusId == questionStatusId))
        .Include(a => a.Answers)
        .ToList();

    var dto = questions.Select(x => new QuestionDto { Title = x.Title ... } );

    return dto; 
}
Run Code Online (Sandbox Code Playgroud)

  • 我想补充一下,对我来说,设置ReferenceLoopHandling.Ignore不起作用,将其设置为globaly或者在API启动时根本不起作用.我设法通过使用[JsonIgnore]装饰子类的navigation属性来使其工作.我仍然得到ParentId但序列化时忽略了父导航. (3认同)

Tal*_*ris 54

你也可以在你的尝试Application_Start():

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
Run Code Online (Sandbox Code Playgroud)

它应该解决你的问题,而不需要经历很多箍.


编辑:根据OttO的评论,使用:ReferenceLoopHandling.Ignore代替.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
Run Code Online (Sandbox Code Playgroud)

  • 我知道这是一个旧线程,但对于那些在将来偶然发现它的人,请尝试:GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; (78认同)
  • 代码进入无限循环并在添加此行后显示堆栈溢出异常. (2认同)

Bon*_*Bon 21

如果使用OWIN,请记住,不再为您提供GlobalSettings!您必须在HttpConfiguration对象中修改此相同设置,该对象将传递给IAppBuilder UseWebApi函数(或您所在的任何服务平台)

看起来像这样.

    public void Configuration(IAppBuilder app)
    {      
       //auth config, service registration, etc      
       var config = new HttpConfiguration();
       config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
       //other config settings, dependency injection/resolver settings, etc
       app.UseWebApi(config);
}
Run Code Online (Sandbox Code Playgroud)


Moh*_*hin 21

在ASP.NET Core中,修复如下:

services
.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
Run Code Online (Sandbox Code Playgroud)


Ste*_*Hou 5

如果使用DNX/MVC 6/ASP.NET vNext blah blah,甚至HttpConfiguration会丢失.您必须使用Startup.cs文件中的以下代码配置格式化程序.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().Configure<MvcOptions>(option => 
        {
            //Clear all existing output formatters
            option.OutputFormatters.Clear();
            var jsonOutputFormatter = new JsonOutputFormatter();
            //Set ReferenceLoopHandling
            jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            //Insert above jsonOutputFormatter as the first formatter, you can insert other formatters.
            option.OutputFormatters.Insert(0, jsonOutputFormatter);
        });
    }
Run Code Online (Sandbox Code Playgroud)

  • 对于.NET Core 1.0 RTM:新的JsonOutputFormatter(serializerSettings,ArrayPool <char> .Shared); (2认同)

Ren*_*elm 5

ASP.NET Core Web-API(.NET Core 2.0):

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Configure<MvcJsonOptions>(config =>
    {
        config.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    });
}
Run Code Online (Sandbox Code Playgroud)