自己参照ループが検出されました-WebApiからブラウザにデータを取り戻します


80

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の応答本文のシリアル化に失敗しました。charset = utf-8 '。

タイプ「Models.Core.Question」のプロパティ「question」に対して自己参照ループが検出されました。

これは、質問に回答があり、回答に質問への参照があるためですか?私が見たすべての場所は、子供の親への言及があることを示唆しているので、私は何をすべきかわかりません。誰かが私にこれについていくつかのアドバイスを与えることができますか?


6
Web APIにDtoを使用し、応答でエンティティが直接返されるのを回避します
cuongle

Dtoとは何ですか?アプリケーション全体でEFを使用し、クライアントでAngularJSを使用していますが、この1つの場合以外は問題ありません。

1
Web ApiのDtoを定義する必要があるということですが、DtoはMVCのViewModelと似ています。Dtoは、クライアント(angularjs)にデータを提供するEFモデルのラッパーのようなものです。
cuongle 2013年


回答:


73

これは、質問に回答があり、回答に質問への参照があるためですか?

はい。シリアル化できません。

編集: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; 
}

3
追加したいのですが、ReferenceLoopHandling.Ignoreの設定が機能しなかった、グローバルに設定した、またはAPIの起動時にまったく機能しなかった。子クラスのナビゲーションプロパティを[JsonIgnore]で装飾することで、なんとか機能させることができました。まだParentIdを取得しますが、シリアル化中に親ナビゲーションが無視されます。
Claiton Lovato 2015年

こんにちは、シリアル化を無視すると、循環依存の質問>回答>質問が壊れます。DTOアプローチはそれを保持しますか?
バルトス2017年

古いASP.NETMVCプロジェクトでこの問題が発生します。GlobalConfiguration.Configurationにはフォーマッターがありません。あなたはそのために何ができるかを提案できますか?
レン

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re‌ferenceLoopHandling = ReferenceLoopHandling.Ignore; ->このコード行をどこに置くか???
anhtv 1320年

56

あなたはまたあなたのでこれを試すことができますApplication_Start()

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

それは多くのフープを経ることなくあなたの問題を解決するはずです。


編集:以下のOttOのコメントに従って、ReferenceLoopHandling.Ignore代わりに:を使用してください。

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

78
私はこれが古いスレッドであることを知っていますが、将来それを見つけた人のために、試してみてください:GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
OttO 2014年

@OttO、あなたの提案は私のために働いた。どうもありがとう。
J86 2014

2
コードは無限ループに入り、この行を追加した後、スタックオーバーフロー例外が表示されます。
Microsoft Developer

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; うまく機能
Dragos Durlut 2016

@DemodaveCreate()は、設定をパラメーターとして受け入れる静的メソッドを使用してJsonSerializerを作成する必要があります。ドキュメント:newtonsoft.com/json/help/html/...
Tallmaris

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

1
あなたは私の日を救った。上記の答えがうまくいかなかったのはなぜだろうと思っていました。はい、Global.asaxでOWIN設定を使用することはできません。
Sithu 2016年

21

ASP.NET Coreでの修正は、次のとおりです。

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

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

1
ASP-ネットRC-1-最終的に私はそれが"services.Configure <MvcOptions>"今であると信じて
のMichałW.

JsonOutputFormatterは名前空間Microsoft.AspNet.Mvc.Formattersにあります
Sam

2
.NET Core 1.0 RTMの場合:new JsonOutputFormatter(serializerSettings、ArrayPool <char> .Shared);
aherrick 2016

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

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


1

ReferenceLoopHandling.Ignoreが機能しませんでした。それを回避する唯一の方法は、コードを介して、不要な親へのリンクを削除し、実行したリンクを保持することでした。

parent.Child.Parent = null;

1

.Net Framework4.5を使用する新しいAsp.NetWebアプリケーションの場合:

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

1

ASP.NET Core 3.0の一部として、チームはデフォルトでJson.NETを含めることをやめました。一般的には、[Json.Netをnetcore3.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;
        });

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

0

このエラーは、既存のデータベースのedmx(概念モデルを定義するXMLファイル)を生成したときに発生し、親テーブルと子テーブルの両方へのナビゲーションプロパティがあることがわかりました。子にのみナビゲートしたかったので、親オブジェクトへのすべてのナビゲーションリンクを削除し、問題は解決しました。


0

エンティティdb = new Entities()

db.Configuration.ProxyCreationEnabled = false;

db.Configuration.LazyLoadingEnabled = false;


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

0

上記の回答の構成はいずれも、ASP.NET Core2.2では機能しませんでした。

JsonIgnore仮想ナビゲーションプロパティに属性を追加しました。

public class Question
{
    public int QuestionId { get; set; }
    public string Title { get; set; }
    [JsonIgnore]
    public virtual ICollection<Answer> Answers { get; set; }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.