別のオブジェクトへの循環参照を持つオブジェクトがあります。これらのオブジェクト間の関係を考えると、これは適切な設計です。
説明する
Machine => Customer => Machine
予想通り、Jsonを使用してマシンまたはカスタマーオブジェクトをシリアル化しようとすると、問題が発生します。 MachineオブジェクトとCustomerオブジェクトの間の関係を壊したくないので、この問題をどのように解決すればよいかわかりません。この問題を解決するためのオプションは何ですか?
編集
現在、私は Controller基本クラスによって提供されるJSONメソッド を使用しています。だから私がしているシリアライゼーションは次のように基本的です:
Json(machineForm);
更新:
NonSerializedAttribute
は明らかにそれを無視するため、JavaScriptSerializer
を使用しないでください。
代わりに、System.Web.Script.Serialization
のScriptIgnoreAttribute
を使用してください。
public class Machine
{
public string Customer { get; set; }
// Other members
// ...
}
public class Customer
{
[ScriptIgnore]
public Machine Machine { get; set; } // Parent reference?
// Other members
// ...
}
このように、Machine
をJson
メソッドにトスすると、それはMachine
からCustomer
への関係をトラバースしますが、元に戻ろうとしませんCustomer
からMachine
へ。
関係はコードが満足するようにコードで実行するためにまだ存在しますが、JavaScriptSerializer
(Json
メソッドで使用)はそれを無視します。
「json.encode循環参照」に対するGoogleからの(現在)3番目の結果であり、上記の回答に(完全に)同意しませんが、ScriptIgnoreAttributeを使用すると、コード内のどこかで、JSONの関係を逆方向にたどることはありません。ユースケースが1つしかないため、モデルをロックダウンするとは思いません。
このシンプルなソリューションを使用することに刺激を受けました。
MVCのビューで作業しているので、モデルがあり、モデルをコントローラー内のViewData.Modelに単純に割り当て、ビュー内でLINQクエリを使用してデータを適切にフラット化し、問題のあるものを削除しますこのようにしたい特定のJSONの循環参照:
var jsonMachines = from m in machineForm
select new { m.X, m.Y, // other Machine properties you desire
Customer = new { m.Customer.Id, m.Customer.Name, // other Customer properties you desire
}};
return Json(jsonMachines);
または、マシン->顧客関係が1 .. *-> *の場合は、次を試してください。
var jsonMachines = from m in machineForm
select new { m.X, m.Y, // other machine properties you desire
Customers = new List<Customer>(
(from c in m.Customers
select new Customer()
{
Id = c.Id,
Name = c.Name,
// Other Customer properties you desire
}).Cast<Customer>())
};
return Json(jsonMachines);
Txlの回答に基づいて、遅延読み込みとプロキシ作成を無効にする必要があり、通常の方法を使用してデータを取得できます。
例:
//Retrieve Items with Json:
public JsonResult Search(string id = "")
{
db.Configuration.LazyLoadingEnabled = false;
db.Configuration.ProxyCreationEnabled = false;
var res = db.Table.Where(a => a.Name.Contains(id)).Take(8);
return Json(res, JsonRequestBehavior.AllowGet);
}
同じ問題がある場合に使用します。 L2EオブジェクトをIDictionaryに「フラット化」する単純な拡張メソッドを作成しました。 IDictionaryはJavaScriptSerializerによって正しくシリアル化されます。結果のJsonは、オブジェクトを直接シリアル化するのと同じです。
シリアライゼーションのレベルを制限しているので、循環参照は避けられます。 1-> nリンクテーブル(エンティティセット)も含まれません。
private static IDictionary<string, object> JsonFlatten(object data, int maxLevel, int currLevel) {
var result = new Dictionary<string, object>();
var myType = data.GetType();
var myAssembly = myType.Assembly;
var props = myType.GetProperties();
foreach (var prop in props) {
// Remove EntityKey etc.
if (prop.Name.StartsWith("Entity")) {
continue;
}
if (prop.Name.EndsWith("Reference")) {
continue;
}
// Do not include lookups to linked tables
Type typeOfProp = prop.PropertyType;
if (typeOfProp.Name.StartsWith("EntityCollection")) {
continue;
}
// If the type is from my Assembly == custom type
// include it, but flattened
if (typeOfProp.Assembly == myAssembly) {
if (currLevel < maxLevel) {
result.Add(prop.Name, JsonFlatten(prop.GetValue(data, null), maxLevel, currLevel + 1));
}
} else {
result.Add(prop.Name, prop.GetValue(data, null));
}
}
return result;
}
public static IDictionary<string, object> JsonFlatten(this Controller controller, object data, int maxLevel = 2) {
return JsonFlatten(data, maxLevel, 1);
}
私のアクションメソッドは次のようになります。
public JsonResult AsJson(int id) {
var data = Find(id);
var result = this.JsonFlatten(data);
return Json(result, JsonRequestBehavior.AllowGet);
}
Entity Framework version 4 には、利用可能なオプションがあります:ObjectContextOptions.LazyLoadingEnabled
Falseに設定すると、「循環参照」の問題が回避されます。ただし、含めるナビゲーションプロパティを明示的にロードする必要があります。
参照してください: http://msdn.Microsoft.com/en-us/library/bb896272.aspx
私の知る限りでは、オブジェクト参照をシリアル化することはできませんが、コピーだけは次のような汚れたハックを使用して試すことができます。
どちらが「ルート」オブジェクトであるかを決定する必要があります。マシンがルートであるとすると、顧客はマシンのサブオブジェクトです。マシンをシリアル化すると、JSONのサブオブジェクトとして顧客がシリアル化され、顧客がシリアル化されると[〜#〜] not [〜#〜]シリアル化されますマシンへの参照。コードがマシンをデシリアライズすると、マシンのカスタマーサブオブジェクトがデシリアライズされ、カスタマーからマシンへの後方参照が復元されます。
ほとんどのシリアライゼーションライブラリは、各クラスのデシリアライゼーションの実行方法を変更するための一種のフックを提供します。そのフックを使用して、マシンクラスの逆シリアル化を変更し、マシンの顧客の後方参照を復元する必要があります。そのフックが正確に何であるかは、使用しているJSONライブラリによって異なります。
私がやったことは少し過激ですが、プロパティが必要ないため、循環参照の原因となる厄介なエラーが発生するため、シリアル化する前にnullに設定しました。
SessionTickets result = GetTicketsSession();
foreach(var r in result.Tickets)
{
r.TicketTypes = null; //those two were creating the problem
r.SelectedTicketType = null;
}
return Json(result);
プロパティが本当に必要な場合は、循環参照を保持しないが、後で元の値を復元するために使用できる重要な要素のIDを保持するビューモデルを作成できます。
今週も同じ問題があり、List<MyType>
を要求するインターフェイスを実装する必要があったため、匿名型を使用できませんでした。ナビゲート可能なすべての関係を示す図を作成した後、MyType
はMyObject
と双方向の関係を持っていることがわかりました。これは、両方がお互いに保存しているためです。
MyObject
は実際にMyType
を知る必要がないと判断し、それによって単方向の関係にした後、この問題は解決されました。