6

MVC3、Razor ビュー エンジン、Unit of Work を含むリポジトリ パターンを使用し、EF4.1 Code First を使用してデータ モデルを定義するアプリケーションを構築しています。

ここに背景が少しあります (必要に応じてそれを塗りつぶしてください)。

アプリケーション自体は、単なるイントラネットの「メニュー」です。

2 つの主要なエンティティは、MenuItem と Department です。

  • MenuItem は多くの部門を持つことができます
  • 部門は多くの MenuItems を持つことができます
  • MenuItem は MenuItem を親として持つことができます

これが私のエンティティを定義した方法です

public class MenuItem
{
   public int MenuItemId { get; set; }
   public string Name { get; set; }
   public string Url { get; set; }
   public virtual ICollection<Department> Departments { get; set; }
   public int? ParentId { get; set; }
   public virtual MenuItem ParentMenuItem { get; set; }
}

public class Department
{
   public int DepartmentId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<MenuItem> MenuItems { get; set; }
}

FluentAPI を使用して、MenuItem の Self Reference Many-to-Many を定義しています。

私が抱えている問題は、JSON を介してビューに MenuItem を渡すことです。中心的な問題は、組み込みの JSON パーサーが処理できないエンティティ間の循環参照があり、遅延読み込みとプロキシ生成がまだ有効になっていることです。

NugetのJSON.netライブラリをJSONシリアライザーとして使用しています。これは、循環参照の問題を回避する良い方法のようです。プロキシ生成の問題を「修正」する方法がわかりません。現在、シリアライザーはスローしますThe RelationshipManager object could not be serialized. This type of object cannot be serialized when the RelationshipManager belongs to an entity object that does not implement IEntityWithRelationships.

誰でもこれで私を助けることができますか?プロキシ生成をオフにすると、すべての MenuItem の子をロードするのに非常に時間がかかるため、オンのままにしておきます。私はかなりの量を読みましたが、エンティティを別のオブジェクトに投影してそれをシリアル化するなど、さまざまな異なる答えがあるようです。理想的には、JSON.net を設定して RelationshipManager オブジェクトを無視する方法がありますか?

アップデート

これは、JSON.Net シリアライザーのカスタム ContractResolver として使用したものです。これで私の問題は解決したようです。

public class ContractResolver : DefaultContractResolver
{
    private static readonly IEnumerable<Type> Types = GetEntityTypes();
    private static IEnumerable<Type> GetEntityTypes()
    {
        var assembly = Assembly.GetAssembly(typeof (IEntity));
        var types = assembly.GetTypes().Where(t => String.Equals(t.Namespace, "Namespace", StringComparison.Ordinal));
        return types;
    }

    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        if (!AllowType(objectType))
            return new List<MemberInfo>();

        var members = base.GetSerializableMembers(objectType);
        members.RemoveAll(memberInfo => (IsMemberEntityWrapper(memberInfo)));
        return members;
    }

    private static bool AllowType(Type objectType)
    {
        return Types.Contains(objectType) || Types.Contains(objectType.BaseType);
    }

    private static bool IsMemberEntityWrapper(MemberInfo memberInfo)
    {
        return memberInfo.Name == "_entityWrapper";
    }
}

IEntityは、すべての Code First エンティティ オブジェクトが実装するインターフェイスです。

4

2 に答える 2

2

この質問には受け入れられた回答があることは承知していますが、将来の閲覧者のために EF Code First ソリューションを投稿すると思いました。以下のコントラクト リゾルバを使用して、エラー メッセージを回避できました。

 class ContractResolver : DefaultContractResolver
 {
      protected override List<System.Reflection.MemberInfo> GetSerializableMembers(Type objectType)
      {
           if (objectType.Namespace.StartsWith("System.Data.Entity.Dynamic"))
           {
                return base.GetSerializableMembers(objectType.BaseType);
           }

           return base.GetSerializableMembers(objectType);
      }
 }

これが機能するのは、EF Code First クラスが、実際にシリアル化する必要がある POCO クラスから継承されるためです。そのため、(名前空間をチェックすることによって) EF によって生成されたクラスを見ているときに識別できれば、ベースのプロパティを使用してシリアル化することができます。クラスであるため、最初に本当に求めていた POCO プロパティのみをシリアル化します。

于 2012-03-10T19:44:25.847 に答える
0

さて、あなたは参照とすべてのメンバーもシリアル化する強力なシリアル化APIを使用しましたが、今ではすべてのメンバーをシリアル化すると文句を言います:)

私はそれをテストしませんでしたが、これはあなたを解決策に近づけると信じています。

JSON.NETは非常に強力なツールであり、この動作を回避するための拡張ポイントを提供する必要がありますが、自分でコーディングする必要があります。DataContractResolverシリアル化するメンバーを定義するカスタムが必要になります。これはNHibernateの同様の例です。

動的プロキシの親クラスに存在するメンバーのみを取得するロジックを実装できます。これで遅延読み込みが中断されないことを願っています。現在のエンティティがプロキシであることを検証するには、次のコードを使用して、すべての既知のプロキシタイプを取得できます。

IEnumerable<Type> types = ((IObjectContextAdapter)dbContext).ObjectContext.GetKnownProxyTypes();
于 2011-08-09T08:51:57.657 に答える