タイプ 'SubSonic.Schema .DatabaseColumn'のオブジェクトのシリアル化中に循環参照が検出されました。


170

単純なJSONを返そうとしていますが、以下の問題があります。

public JsonResult GetEventData()
{
    var data = Event.Find(x => x.ID != 0);
    return Json(data);
}

この質問のタイトルに示されているように、例外としてHTTP 500を取得します。私も試しました

var data = Event.All().ToList()

同じ問題が発生しました。

これはバグですか、それとも私の実装ですか?


1
これ見て ScriptIgnore属性を使用した解決策があります。stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt
freddoo 2009年

これは私にとって最良の解決策でした。私は、など私が置かれ、ゲーム>トーナメント>ゲーム>トーナメント>ゲームを持っていたScriptIgnore:) Tournament.Gameプロパティに属性をし、それがうまく働いた
eth0の

追加のコードを必要としないこの問題の「自動化された」(ベストプラクティスではない)ソリューションが必要な場合は、次のQAを確認してください
nikib3ro

回答:


175

JSONシリアライザーでサポートされていないオブジェクト階層に循環参照があるようです。すべての列が必要ですか?ビューで必要なプロパティのみを取得できます。

return Json(new 
{  
    PropertyINeed1 = data.PropertyINeed1,
    PropertyINeed2 = data.PropertyINeed2
});

これにより、JSONオブジェクトが軽くなり、理解しやすくなります。多くのプロパティがある場合、AutoMapperを使用して、DTOオブジェクトとビューオブジェクトを自動的にマッピングできます。


私が思うに、多分私が欲しいものは働くかもしれ選択私は循環参照が順番にのIQueryable <イベント>があります。イベントIに持っているためのIQueryable <カテゴリ>だと思う
ジョン・

7
Automapperは、この問題が発生しないことを保証するものではありません。私は答えを求めてここに来て、実際にオートマッパーを使用しています。
船長

1
@ClayKaboomからの回答が円形である理由を説明しているのでご覧ください
PandaWood

106

私は同じ問題を抱えて解決しました using Newtonsoft.Json;

var list = JsonConvert.SerializeObject(model,
    Formatting.None,
    new JsonSerializerSettings() {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});

return Content(list, "application/json");

3
このインラインコードは私にとってはうまくいきました。kravits88で言及されているものと同じグローバル構成のものは、私には機能しません。また、このコードのContentResultを返すようにメソッドシグネチャを更新する必要があります。
BiLaL 2015

6
これは、承認済みとしてマークされた回答のように、オブジェクトを他の表現に変換するのに何時間も費やすことができない場合をカバーするため、最良の回答としてマークする必要があります。
Renan

56

複雑なオブジェクトが結果のjsonオブジェクトを失敗させる原因となっているため、これは実際に起こります。また、オブジェクトがマップされると、子がマップされ、親がマップされ、循環参照が発生するため、失敗します。Jsonはシリアル化に無限の時間を要するため、例外の問題を回避できます。

エンティティフレームワークマッピングも同じ動作を生成し、解決策はすべての不要なプロパティを破棄することです。

最終的な答えを明示するだけで、コード全体は次のようになります。

public JsonResult getJson()
{
    DataContext db = new DataContext ();

    return this.Json(
           new {
                Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
               }
           , JsonRequestBehavior.AllowGet
           );
}

Resultプロパティ内のオブジェクトが不要な場合は、次のようになります。

public JsonResult getJson()
{
    DataContext db = new DataContext ();

    return this.Json(
           (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
           , JsonRequestBehavior.AllowGet
           );
}

1
+1を使用すると、わかりやすく理解しやすくなります。@ Clayに感謝します。エラーの背後にある概念についての説明が好きです。
Ajay2707 2017

14

まとめると、これには4つの解決策があります。

解決策1:DBContextのProxyCreationをオフにして、最後に復元します。

    private DBEntities db = new DBEntities();//dbcontext

    public ActionResult Index()
    {
        bool proxyCreation = db.Configuration.ProxyCreationEnabled;
        try
        {
            //set ProxyCreation to false
            db.Configuration.ProxyCreationEnabled = false;

            var data = db.Products.ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
        finally
        {
            //restore ProxyCreation to its original state
            db.Configuration.ProxyCreationEnabled = proxyCreation;
        }
    }

解決策2:ReferenceLoopHandlingを設定してJsonConvertを使用し、シリアライザー設定を無視する。

    //using using Newtonsoft.Json;

    private DBEntities db = new DBEntities();//dbcontext

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.ToList();

            JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
            var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss);

            return Json(result, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }

以下の2つのソリューションは同じですが、強い型付けがされているため、モデルを使用する方が適しています。

解決策3:必要なプロパティのみを含むモデルを返す。

    private DBEntities db = new DBEntities();//dbcontext

    public class ProductModel
    {
        public int Product_ID { get; set;}

        public string Product_Name { get; set;}

        public double Product_Price { get; set;}
    }

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.Select(p => new ProductModel
                                                {
                                                    Product_ID = p.Product_ID,
                                                    Product_Name = p.Product_Name,
                                                    Product_Price = p.Product_Price
                                                }).ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }

解決策4:必要なプロパティのみを含む新しい動的オブジェクトを返す。

    private DBEntities db = new DBEntities();//dbcontext

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.Select(p => new
                                                {
                                                    Product_ID = p.Product_ID,
                                                    Product_Name = p.Product_Name,
                                                    Product_Price = p.Product_Price
                                                }).ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }

7

JSONは、xmlやその他のさまざまな形式と同様に、ツリーベースのシリアル化形式です。「ツリー」は次のようになるので、オブジェクトに循環参照がある場合、それはあなたを愛しません。

root B => child A => parent B => child A => parent B => ...

多くの場合、特定のパスに沿ったナビゲーションを無効にする方法があります。たとえば、ではXmlSerializer、親プロパティをとしてマークすることができますXmlIgnore。これが問題のJSONシリアライザで可能であるならば、私にはわからない、またかどうかは、DatabaseColumn(適切なマーカーを持っている非常にそれがすべての直列化APIを参照する必要があるだろうと、そう)


4

これは、EntityFrameworkエンティティの生成に使用される新しいDbContext T4テンプレートが原因です。変更の追跡を実行できるようにするために、このテンプレートは、素敵なPOCOをラップして、プロキシパターンを使用します。これにより、JavaScriptSerializerでシリアル化するときに問題が発生します。

したがって、2つのソリューションは次のとおりです。

  1. クライアントで必要なプロパティをシリアル化して返すか
  2. コンテキストの構成で設定することにより、プロキシの自動生成をオフにすることができます

    context.Configuration.ProxyCreationEnabled = false;

以下の記事で非常によく説明されています。

http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/


4

Newtonsoft.Jsonの使用:Global.asax Application_Startメソッドで、次の行を追加します。

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

1
どうやら非常にまっすぐに見えますが、私には
うまくいき


4

テーブルオブジェクトを直接変換しないでください。他のテーブル間にリレーションが設定されている場合、このエラーがスローされる可能性があります。むしろ、モデルクラスを作成し、クラスオブジェクトに値を割り当てて、それをシリアル化できます。


3

提供された回答は良いですが、「建築」の視点を追加することで改善できると思います。

調査

MVC's Controller.Json関数は仕事をしていますが、この場合、関連するエラーを提供することは非常に貧弱です。を使用するNewtonsoft.Json.JsonConvert.SerializeObjectと、エラーは循環参照をトリガーしているプロパティを正確に特定します。これは、より複雑なオブジェクト階層をシリアル化する場合に特に便利です。

適切なアーキテクチャ

ORMのナビゲーションプロパティはシリアル化に関してperditionへの道であるため、データモデル(EFモデルなど)のシリアル化を試みてはいけません。データフローは次のようになります。

Database -> data models -> service models -> JSON string 

サービスモデルは、自動マッパー(Automapperなど)を使用してデータモデルから取得できます。)。これは循環参照の欠如を保証するものではありませんが、適切な設計はそれを行う必要があります。サービスモデルには、サービスユーザーが必要とするもの(つまり、プロパティ)を正確に含める必要があります。

まれなケースですが、クライアントが異なるレベルの同じオブジェクトタイプを含む階層を要求すると、サービスは親と子の関係を持つ線形構造を作成できます(参照ではなく識別子のみを使用)。

最近のアプリケーションは、複雑なデータ構造を一度にロードすることを避ける傾向があり、サービスモデルはスリムでなければなりません。例えば:

  1. イベントにアクセス-ヘッダーデータのみ(識別子、名前、日付など)が読み込まれます->ヘッダーデータのみを含むサービスモデル(JSON)
  2. 管理対象の出席者リスト-ポップアップにアクセスしてリストを遅延ロードします->出席者のリストのみを含むサービスモデル(JSON)

1

MVC5ビューでノックアウトを使用しているため、私は修正を使用しています。

行動中

return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));

関数

   public static TEntity GetJsonModel<TEntity>(TEntity Entity) where TEntity : class
    {
        TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity;
        foreach (var item in Entity.GetType().GetProperties())
        {
            if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1)
                item.SetValue(Entity_, Entity.GetPropValue(item.Name));
        }
        return Entity_;  
    }

0

循環参照を引き起こすプロパティに気付くでしょう。次に、次のようなことができます:

private Object DeCircular(Object object)
{
   // Set properties that cause the circular reference to null

   return object
}

-1
//first: Create a class as your view model

public class EventViewModel 
{
 public int Id{get;set}
 public string Property1{get;set;}
 public string Property2{get;set;}
}
//then from your method
[HttpGet]
public async Task<ActionResult> GetEvent()
{
 var events = await db.Event.Find(x => x.ID != 0);
 List<EventViewModel> model = events.Select(event => new EventViewModel(){
 Id = event.Id,
 Property1 = event.Property1,
 Property1 = event.Property2
}).ToList();
 return Json(new{ data = model }, JsonRequestBehavior.AllowGet);
}

これは質問の答えにはなりません
デーンI

-1

この問題を解決する簡単な方法は、文字列を返し、その文字列をJavaScriptSerializerでjsonにフォーマットすることです。

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
   return j.Serialize(entityList );
}

ビューで必要なプロパティを選択する「選択」部分は重要です。一部のオブジェクトには、親の参照があります。属性を選択しなかった場合、テーブル全体をそのまま使用すると、循環参照が表示されることがあります。

こんなことしないで:

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.toList();
   return j.Serialize(entityList );
}

テーブル全体が必要ない場合は、代わりにこれを実行してください。

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
   return j.Serialize(entityList );
}

これにより、必要な属性だけを使用して、少ないデータでビューをレンダリングし、Webをより高速に実行できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.