簡體   English   中英

檢測到自引用循環 - 將數據從 WebApi 返回到瀏覽器

[英]Self referencing loop detected - Getting back data from WebApi to the browser

我正在使用實體框架並且在將父子數據獲取到瀏覽器時遇到問題。 這是我的課程:

 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”的屬性“question”的自引用循環。

這是因為問題有答案並且答案有對問題的引用嗎? 我看過的所有地方都建議參考孩子的父母,所以我不知道該怎么做。 有人可以就此給我一些建議。

這是因為問題有答案並且答案有對問題的引用嗎?

是的。 它不能被序列化。

編輯:請參閱 Tallmaris 的回答和 OttO 的評論,因為它更簡單並且可以全局設置。

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

舊答案:

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

就像是:

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;

如果使用 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);
}

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

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

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

如果使用 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);
        });
    }

使用這個:

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

ReferenceLoopHandling.Ignore 對我不起作用。 我能繞過它的唯一方法是通過代碼刪除鏈接回我不想要的父級並保留我做的那些。

parent.Child.Parent = null;

對於使用 .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 }
        );
    }
}

作為 ASP.NET Core 3.0 的一部分,團隊不再默認包含 Json.NET。 您可以在 [包括 Json.Net 到 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;
        });

由於延遲加載,您會收到此錯誤。 因此我的建議是從財產中刪除虛擬鑰匙。 如果您正在使用 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 文件)時引起的,並且它具有到父表和子表的導航屬性。 我刪除了所有到父對象的導航鏈接,因為我只想導航到子對象,問題就解決了。

實體數據庫 = 新實體()

db.Configuration.ProxyCreationEnabled = false;

db.Configuration.LazyLoadingEnabled = false;

您可以動態創建新的子集合來輕松解決此問題。

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

上面答案中的所有配置都不適用於 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; }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM