83

我正在使用 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; }
}

我正在使用以下代码返回问答数据:

    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”的响应正文;字符集=utf-8'。

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

这是因为问题有答案,而答案有对问题的引用吗?我看过的所有地方都建议在孩子身上提到父母,所以我不知道该怎么做。有人可以给我一些建议吗?

4

15 回答 15

74

Is this because the Question has Answers and the Answers have a reference back to Question?

Yes. It cannot be serialized.

EDIT: See Tallmaris's answer and OttO's comment as it is simpler and can be set globally.

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

Old Answer:

Project the EF object Question to your own intermediate or DataTransferObject. This Dto can then be serialized successfully.

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

Something like:

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; 
}
于 2013-06-26T07:29:48.510 回答
56

你也可以在你的Application_Start()

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

它应该可以解决您的问题,而无需经历很多麻烦。


编辑:根据 OttO 在下面的评论,请改用:ReferenceLoopHandling.Ignore

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
于 2013-06-26T08:27:22.403 回答
21

如果使用 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);
}
于 2015-08-11T15:59:10.063 回答
21

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

services
.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
于 2016-11-09T06:53:06.173 回答
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;
    });
}
于 2018-03-16T17:34:44.583 回答
4

如果使用 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);
        });
    }
于 2015-09-15T07:56:17.220 回答
3

作为 ASP.NET Core 3.0 的一部分,团队默认不再包含 Json.NET。您可以在 [Including Json.Net to netcore 3.x][1] https://github.com/aspnet/Announcements/issues/325中阅读更多相关信息

使用延迟加载可能会导致错误: services.AddDbContext(options => options.UseLazyLoadingProxies()... 或 db.Configuration.LazyLoadingEnabled = true;

修复:添加到 startup.cs

 services.AddControllers().AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });
于 2020-04-13T20:57:18.380 回答
2

使用这个:

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

于 2015-06-23T17:24:28.500 回答
1

ReferenceLoopHandling.Ignore 对我不起作用。我可以绕过它的唯一方法是通过代码将链接删除回我不想要的父级并保留我所做的那些。

parent.Child.Parent = null;
于 2015-08-07T10:55:33.630 回答
1

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

Web Api:转到 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 }
        );
    }
}
于 2017-10-27T14:29:20.247 回答
0

由于延迟加载,您会收到此错误。因此我的建议是从财产中删除虚拟钥匙。如果您使用 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; }
}
于 2015-10-07T15:06:58.543 回答
0

我发现当我生成现有数据库的 edmx(定义概念模型的 XML 文件)并且它具有父表和子表的导航属性时,会导致此错误。我删除了所有指向父对象的导航链接,因为我只想导航到子对象,问题就解决了。

于 2016-09-01T13:09:08.847 回答
0

您可以动态创建新的子集合以轻松解决此问题。

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; 
    }
于 2017-12-17T22:08:33.027 回答
0

实体 db = 新实体()

db.Configuration.ProxyCreationEnabled = false;

db.Configuration.LazyLoadingEnabled = false;

于 2017-02-21T07:02:19.993 回答
0

上述答案中的所有配置在 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; }
}
于 2019-05-02T22:05:30.510 回答