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

我正在使用实体框架,并与获得父母和子女的数据浏览器有一个问题。这是我的课程:

 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; }
}

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

    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;
}

在 C # 方面,这似乎是有效的,但是我注意到,答案对象有对问题的引用。当我使用 WebAPI 将数据传送到浏览器时,我会得到以下消息:

‘ ObjectContent‘1’类型未能序列化内容类型‘ application/json; charset = utf-8’的响应正文。

检测到类型为“ Models.Core.questions”的属性“ questions”的自引用循环。

这是因为问题有答案,而答案有一个参考回到问题?所有我看过的地方都建议在孩子的父母那里有一个参考,所以我不知道该怎么做。有人能给我点建议吗。

89359 次浏览

这是因为问题有答案,而答案有一个 回到问题?

是的,不能连载。

编辑: 见 Tallmaris 的答案和 OttO 的评论,因为它更简单,可以全球设置。

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re‌​ferenceLoopHandling = ReferenceLoopHandling.Ignore;

老答案:

将 EF 对象 Question投影到您自己的中间对象或 DataTransferObject 中。

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; }
}

比如:

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;
}

你也可以在你的 Application_Start()中试试这个:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;

它应该可以解决你的问题,而不需要经历太多的障碍。


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

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

用这个:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore

对我不起作用。相反,我创建了一个新的简化版本的模型类来进行测试,结果很好。这篇文章介绍了我的模型中的一些问题,这些问题对 EF 很有用,但是不能连载:

Http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4

参考循环处理。无视对我没用。我唯一能绕过它的方法就是通过代码删除那些我不想要的链接,保留那些我想要的。

parent.Child.Parent = null;

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

看起来像这样。

    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);
}

如果使用 DNX/MVC 6/ASP.NET vNext 等等,甚至缺少 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);
});
}

由于延迟加载,您会得到这个错误。因此,我的建议是从属性中删除虚拟键。如果您使用的是 API,那么延迟加载不利于您的 API 健康。

无需在配置文件中添加额外的行。

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


public class Answer
{
public int AnswerId { get; set; }
public string Text { get; set; }
public int QuestionId { get; set; }
public Question Question { get; set; }
}

我发现这个错误是因为我生成了一个现有数据库的 edmx (定义了一个概念模型的 XML 文件) ,它对父表和子表都有导航属性。我删除了所有指向父对象的导航链接,因为我只想导航到子对象,这样问题就解决了。

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

services
.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

实体 db = 新实体()

配置

配置: LazyLoadingEnable = false;

对于使用.Net Framework 4.5的新 Asp.Net Web 应用程序:

Web API: Goto App _ Start-> WebApiConfig.cs:

应该是这样的:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));


// ReferenceLoopHandling.Ignore will solve the Self referencing loop detected error
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;


//Will serve json as default instead of XML
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));


// Web API routes
config.MapHttpAttributeRoutes();


config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}

您可以动态地创建一个新的子集合来轻松地解决这个问题。

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).Select(b=> new {
b.QuestionId,
b.Title
Answers = b.Answers.Select(c=> new {
c.AnswerId,
c.Text,
c.QuestionId }))
.ToList();
return questions;
}

NET 核心 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;
});
}

在 ASP.NET Core 2.2中,以上答案中的配置都不适合我。

我在虚拟导航属性上添加了 JsonIgnore属性。

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

作为 ASP.NET Core 3.0的一部分,该团队已经不再默认包含 Json.NET。你可以在[包括 Json ]一书中读到更多关于这方面的内容。网络呼叫网核3。X ][1] https://github.com/aspnet/Announcements/issues/325

一个错误可能是由于你使用了延迟加载: 添加 DbContext (options = > options. UseLazyLoadingProxy () ..。 或者 配置: LazyLoadingEnable = true;

修正: 添加到 startup.cs

 services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});

我认为在大多数情况下,如果您有这个问题,修改序列化程序只会治疗症状。

如果可能的话,最好修复基础数据源或调用代码。显然,如果你不能控制这些,除了退出,你什么也做不了:)