this postで述べたように、Entity Framework Proxyのシリアル化中にJsonシリアル化エラーが発生します。
タイプ 'System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0'のオブジェクトのシリアル化中に循環参照が検出されました。
しかし、違いは、Ido n'tエンティティに循環参照があり、それはonly本番環境で発生します。ローカルではすべて正常に動作します...
私のエンティティ:
public interface IEntity
{
Guid UniqueId { get; }
int Id { get; }
}
public class Entity : IEntity
{
public int Id { get; set; }
public Guid UniqueId { get; set; }
}
public class PurchaseOrder : Entity
{
public string Username { get; set; }
public string Company { get; set; }
public string SupplierId { get; set; }
public string SupplierName { get; set; }
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
}
public class PurchaseOrderLine : Entity
{
public string Code { get; set; }
public string Name { get; set; }
public decimal Quantity { get; set; }
}
例外をスローするPurchaseOrderControllerのGetCurrentアクション:
public class PurchaseOrderController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public PurchaseOrderController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public JsonResult GetCurrent()
{
return Json(EnsurePurchaseOrder(), JsonRequestBehavior.AllowGet);
}
private PurchaseOrder EnsurePurchaseOrder()
{
var company = RouteData.GetRequiredString("company");
var repository = _unitOfWork.GetRepository<PurchaseOrder>();
var purchaseOrder = repository
.Include(p => p.Lines)
.FirstOrDefault
(
p => p.Company == company &&
p.Username == User.Identity.Name
);
if (purchaseOrder == null)
{
purchaseOrder = repository.Create();
purchaseOrder.UniqueId = Guid.NewGuid();
purchaseOrder.Company = company;
purchaseOrder.Username = User.Identity.Name;
_unitOfWork.SaveChanges();
}
return purchaseOrder;
}
}
POCOエンティティは完全にシリアル化可能です。あなたの問題は、EFランタイムがあなたのために作成する動的プロキシが通常そうではないということです。 context.Configuration.ProxyCreationEnabled
をfalse
に設定できますが、遅延読み込みが失われます。 EFエンティティのシリアル化をサポートするJson.NET
を使用することを強くお勧めします。
オプション1(推奨)
DbContextでプロキシオブジェクトの作成をオフにしてみてください 。
DbContext.Configuration.ProxyCreationEnabled = false;
通常、このシナリオは、アプリケーションがPOCOオブジェクト(T4生成またはCode-First)を使用しているためです。この問題は、Entity FrameworkがPOCOオブジェクトに組み込まれていないオブジェクトの変更を追跡する場合に発生します。これを解決するために、EFはPOCOオブジェクトに属性がなく、シリアル化できないプロキシオブジェクトを作成します。
このアプローチを推奨する理由。 Webサイトを使用するということは、おそらくEntity Frameworkオブジェクトの変更追跡(ステートフル)が必要ないことを意味します。変更追跡が無効になっているため、メモリとCPUが解放され、すべてのオブジェクトで同じように一貫して動作します。
オプション2
カスタマイズしてオブジェクトをシリアル化できるシリアライザー(ASP.Net 4に既に含まれている JSON.Net など)を使用します。
このアプローチを推奨しない理由は、最終的にカスタムオブジェクトシリアル化ロジックがプロキシオブジェクトをotherオブジェクトタイプとしてシリアル化する必要があるためです。これは、結果を下流に配信するためにロジックに依存していることを意味します。オブジェクトの変更とはロジックの変更を意味し、ASP.Net MVCプロジェクト(任意のバージョン)では、ビューを変更するだけでなく、ロジックを最初に書いた人以外には知らない変更があります。
オプション3(Entity Framework 5.x +)
。AsNoTracking() を使用すると、特定のクエリでプロキシオブジェクトが無効になります。変更の追跡を使用する必要がある場合、これにより、ソリューション#1に対するNice中間ソリューションが可能になります。
私は、ウェブ全体に散らばっているさまざまな解決策のすべてを試みるために数え切れないほどの時間を費やしました。
これらはすべて、最終的に私にとって実りのないものでした。プロパティを無視すると、1つのクエリは役立ちましたが、他の3つのクエリは傷つきました。 whack-a-moleに相当するプログラミングのように感じました。
私の問題のコンテキストは、アプリケーションから出て行くデータはJSONでなければならないということでした。それを回避する方法はありません。挿入と更新は明らかに問題を引き起こしません。しかし、正規化されたデータベース(私の場合はバージョン履歴を含む)に保存されているデータを選択してシリアル化するのは悪夢です。
ソリューション:
必要なデータ(プロパティ)を匿名オブジェクトとして返します。
コード例:
この場合、「予定日」に基づいて3つの最新のチケットが必要でした。しかし、関連するエンティティに保存されたいくつかのプロパティも必要でした。
var tickets =
context.TicketDetails
.Where(t => t.DateScheduled >= DateTime.Now)
.OrderBy(t => t.DateScheduled)
.Take(3)
.Include(t => t.Ticket)
.Include(t => t.Ticket.Feature)
.Include(t => t.Ticket.Feature.Property)
.AsEnumerable()
.Select(
t =>
new {
ID = t.Ticket.ID,
Address = t.Ticket.Feature.Property.Address,
Subject = t.Ticket.Subject,
DateScheduled = String.Format("{0:MMMM dd, yyyy}", t.DateScheduled)
}
);
そして出来上がり、自己参照ループはありません。
エンティティとオブジェクトが変更される可能性があることを考えると、この状況はすべての場合に適切ではないかもしれないことを理解しています。しかし、他のすべてが失敗した場合、確かにいくつかの検討に値します。
他のクラスの参照を持つクラスはすべて、このような属性を追加するだけです
[Newtonsoft.Json.JsonIgnoreAttribute]
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
今、すべてがスムーズに動作します
DbContext
クラスで、次のコード行を追加します。
this.Configuration.ProxyCreationEnabled = false;
例えば:
public partial class EmpDBEntities : DbContext
{
public EmpDBEntities()
: base("name=EmpDBEntities")
{
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Department> Departments { get; set; }
public virtual DbSet<Employee> Employees { get; set; }
}
同じエラーが発生しましたが、本番サーバーとローカルの両方で見ました。 DbContext構成を変更しても、私の問題はまったく解決しませんでした。別のソリューションが私に提示されました
[IgnoreDataMember]
dBエンティティ参照の属性。これがあなたの問題により関連すると思われる場合は、こちらの投稿を参照してください。
私は同じ問題を抱えていました、私がやったことは、表示するために必要な列のみを渡したことです、私の場合は。のみ2。
List<SubCategory> lstSubCategory = GetSubCateroy() // list from repo
var subCategoryToReturn = lstSubCategory.Select(S => new { Id = S.Id, Name = S.Name });
return this.Json(subCategoryToReturn , JsonRequestBehavior.AllowGet);
私は同じ問題を抱えており、Reference ManagerのプロジェクトExtensionsでJson.NETのチェックを外すことで解決しました。
(画像を参照してください http://i.stack.imgur.com/RqbXZ.png )
また、project.csprojファイルを変更して、新しいバージョンの正しいパスをマップする必要がありました。
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
それでもweb.configを設定する必要がありました
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
Web.configファイルでは、インストールされたバージョンは6.0.5でしたが、OLDER(6.0.0.0)バージョンを参照するように強制されていたことに注意してください。
それが役に立てば幸い!
循環参照が発生するのは、オブジェクトに対して積極的な読み込みを使用しているためです。
次の3つの方法があります。
PS。プロキシは、Entity Frameworkから読み込むときにEFによって作成されるオブジェクトです。つまり、元の値と更新された値を保持して、後で更新できることを意味します。他のことを処理します;-)