6

我正在使用 MVC3、Razor 视图引擎、带有工作单元的存储库模式以及使用 EF4.1 Code First 来定义我的数据模型来构建应用程序。

这里有一点背景(如果你愿意,可以涂上光泽)。

应用程序本身只是一个 Intranet '菜单'。

两个主要实体是 MenuItem 和 Department ,其中:

  • MenuItem 可以有很多部门
  • 部门可以有很多菜单项
  • 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 定义自引用多对多。

我遇到的问题是通过 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 回答