2009-07-20 8 views
151

私は単純なJSONリターンをしようとしていますが、私は以下の問題があります。'SubSonic.Schema .DatabaseColumn'型のオブジェクトをシリアル化しているときに循環参照が検出されました。

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

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

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

それは同じ問題をもたらしました。

これはバグですか、実装していますか?

+0

場合、誰かが(ないベストプラクティス)「自動化」望んでいる余分なコードを必要としないこの問題の解決策、このQAをチェックアウト:[JSONで(ServiceStack.Textライブラリ)をEntity Frameworkのクラス参照をシリアライズしない](HTTP ://stackoverflow.com/questions/14998890/do-not-serialize-entity-framework-class-references-in-json-servicestack-text-li/15010306#15010306)この1で – kape123

+1

ルック。 'ScriptIgnore'属性を使った解決策があります。 [stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt] (http://stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt) – freddoo

+0

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

答えて

158

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

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

これは、JSONオブジェクトをより軽く分かりやすくします。多くのプロパティがある場合は、AutoMapperを使用して、DTOオブジェクトとViewオブジェクトの間のマップをautomaticallyにマップできます。

+0

私は多分私が働くかもしれないものを選択すると思います。イベントでは、私が持っているので、今度はのIQueryable Jon

+7

Automapperを持つことになりますのIQueryable は、あなたがこの問題を取得することはありません保証するものではありません。私はここで答えを探しに来て、私は実際にオートマッペを使っています。 –

+1

@ClayKaboomからの回答をご覧ください。理由は、循環性がなぜ – PandaWood

6

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

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

あり、多くの場合、特定のパスに沿ったナビゲーションを無効にする方法。たとえば、XmlSerializerとすると、親プロパティをXmlIgnoreとマークすることがあります。 (それはすべての直列化APIを参照する必要があるとして、非常にそう)私は、複雑なオブジェクトがあるので、これは実際に起こる

49

をこの問題のJSONシリアライザで可能かどうかを知り、またDatabaseColumnかどうか、適切なマーカーを持っていません結果として生じるjsonオブジェクトが失敗する原因。 オブジェクトがマップされるときに親をマップする子をマップし、循環参照を発生させるので失敗します。 Jsonはシリアル化する無限の時間を取るので、例外の問題を防ぎます。

エンティティフレームワークマッピングでも同じ動作が生成されます。解決策は、不要なプロパティをすべて破棄することです。ただ、最終的な答えをexpliciting

、全体のコードは次のようになります。

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

+1で、わかりやすくわかりやすく、@Clayに感謝します。私はエラーの背後にある概念についてあなたの説明が好きです。 – Ajay2707

4

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

それでは2の解決策は以下のとおりです。

  1. あなたはちょうどあなたがクライアント
  2. に必要なプロパティをシリアル化して返すのどちらかあなたは、コンテキストの設定にそれを設定することにより、プロキシの自動生成をオフにします

    context.Configuration.ProxyCreationEnabled = false;

以下の記事で詳しく説明します。

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

3

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

74

私は同じ問題を抱えていたとusing Newtonsoft.Json; Newtonsoft.Jsonを使用して

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

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

このインラインコードはうまく動作しました。 kravits88で言及されているグローバル設定と同じものが私のために働いていません。また、メソッドのシグネチャを更新して、このコードのContentResultを返す必要があります。 – BiLaL

+2

優れたソリューション..ありがとう –

+3

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

3

によって解決:あなたのGlobal.asaxのApplication_Startメソッドでは、この行を追加します。

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

これは私のために働かなかった – kbo4sho88

+1

明らかに非常にまっすぐに見えるが、私のために働いていない – BiLaL

+1

これは動作しませんでした。 – piris

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_; 
    } 
-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); 
} 
+0

これは質問に答えません –

6

、これまで3つの解決策があります。

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

    //Solution 1: turn off ProxyCreation for the DBContext and restore it in the end 
    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; 
     } 
    } 

    //Solution 2: Using JsonConvert by Setting ReferenceLoopHandling to ignore on the serializer settings. 
    //using using Newtonsoft.Json; 
    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); 
     } 
    } 

    //Solution 3: return a new dynamic object which includes only the needed properties. 
    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); 
     } 
    } 
3

は、あなたの中virtualsをプロパティに[JsonIgnore]を追加モデル。

2

答えは良いですが、私は「建築的」な視点を加えることで改善できると思います。

調査

MVC's Controller.Json機能は、仕事をしているが、それはこの場合には、関連するエラーを提供することを非常に悪いです。 Newtonsoft.Json.JsonConvert.SerializeObjectを使用すると、エラーは循環参照をトリガーするプロパティが正確に指定されます。これは、より複雑なオブジェクト階層をシリアライズする場合に特に便利です。 ORMのナビゲーションプロパティは、それがシリアライズに来るときロード・トゥ・パーディションであるよう

適切なアーキテクチャ

一つは、データモデル(例えば、EFモデル)を直列化しようとすることはありません。データフローは次のようにすべきである:

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

サービスモデルは、自動マッパー(例えばAutomapper)を使用して、データモデルから得ることができます。循環参照の不足を保証するわけではありませんが、サービスモデルにはサービスコンシューマが必要とするもの(つまりプロパティ)を正確に含める必要があります。

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

現代のアプリケーションでは、複雑なデータ構造を一度に読み込まないようにする傾向があり、サービスモデルはスリムでなければなりません。例:

  1. アクセスイベント - ヘッダデータのみ(識別子、名前、日付など)にロードされる - >サービスモデル(JSON)を含むヘッダデータのみが
  2. は、参加者のリストを管理 - ポップアップや遅延ロードにアクセス出席者のリストのみを含むリスト - >サービスモデル(JSON)
0

循環参照の原因となるプロパティがわかります。

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

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

これは、あなただけの属性で、以下のデータを持つビューをレンダリングするのに役立ちます

はこれをしないでくださいあなたのウェブをより速く走らせることができます。

関連する問題