使用 JSON.Net 序列化 EF4.1 实体

发布于 2024-11-28 17:29:44 字数 2593 浏览 2 评论 0原文

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

这是一些背景知识(如果你愿意的话可以忽略它)。

该应用程序本身只是一个内联网“菜单”。

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

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

我遇到的问题是通过 JSON 将 MenuItem 传递到视图。 核心问题是我的实体之间存在循环引用,内置 JSON 解析器无法处理该引用,并且我仍然启用延迟加载和代理生成。

我使用 Nuget 中的 JSON.net 库作为我的 JSON 序列化器,因为这似乎是解决循环引用问题的好方法。我现在不确定如何“修复”代理生成问题。目前,序列化程序抛出TheRelationshipManagerobjectcouldnablenotbeserialized。当RelationshipManager属于未实现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 实体对象实现的接口。

I am building an application using MVC3, Razor view engine, Repository Pattern with Unit of Work and using EF4.1 Code First to define my data model.

Here is a bit of background (gloss over it if you want).

The application itself is just an Intranet 'Menu'.

The 2 main entities are MenuItem and Department of which:

  • MenuItem can have many Departments
  • Departments can have many MenuItems
  • MenuItem may have a MenuItem as a parent

This is how I have defined my Entities

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; }
}

I am using the FluentAPI to define the Self Reference Many-to-Many for the MenuItem.

The issue I am having is passing a MenuItem to the view via JSON.
The central issues are that I have a circular reference between my entities that the built in JSON parser can't deal with and I have lazy loading and proxy generation still enabled.

I am using JSON.net library from Nuget as my JSON Serializer as this seems to be a nice way round the circular reference issue. I now am unsure how to 'fix' the proxy generation issue. Currently the serializer throws 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.

Can anyone help me with this? If I turn off proxy generation, I am going to have a hell of a time loading all of the MenuItem children so I am keen leave this on. I have read a fair amount and there seems to be a variety of different answers including projecting the entities into another object and serialize that, etc, etc. Ideally there would be some way of configuring JSON.net to ignore the RelationshipManager object?

Update

Here is what I have used as a Custom ContractResolver for JSON.Net serializer. This seems to have sorted out my issue.

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 is an interface all my Code First entity objects implement.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我的奇迹 2024-12-05 17:29:44

我意识到这个问题有一个公认的答案,但我想我应该为未来的观众发布我的 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 属性。

I realise this question has an accepted answer, but I thought I would post my EF Code First solution for future viewers. I was able to get around the error message with the contract resolver below:

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

This works because EF Code First classes inherit from the POCO class that you actually want serialized, so if we can identify when we are looking at an EF generated class (by checking the namespace) we are able to just serialize using the properties from the base class, and therefore only serialize the POCO properties that we were really after in the first place.

别再吹冷风 2024-12-05 17:29:44

好吧,您使用了强大的序列化 API,它还序列化了引用和所有成员,现在您抱怨它序列化了所有成员:)

我没有测试它,但我相信这将使您接近解决方案。

JSON.NET 是非常强大的工具,它应该为您提供可扩展点来避免这种行为,但您必须自己编写代码。您将需要自定义DataContractResolver,您可以在其中定义应序列化哪些成员。 此处 是 NHibernate 的类似示例。

您可以实现一些逻辑,该逻辑仅采用动态代理父类中存在的成员。我希望这不会破坏延迟加载。要验证当前实体是代理,您可以使用此代码来获取所有已知的代理类型:

IEnumerable<Type> types = ((IObjectContextAdapter)dbContext).ObjectContext.GetKnownProxyTypes();

Well, you used powerful serialization API which serializes references and all members as well and now you complains that it serializes all members :)

I didn't test it but I believe this will bring you close to the solution.

JSON.NET is quite powerful tool and it should offer you the extensibility point to avoid this behavior but you will have to code it yourselves. You will need custom DataContractResolver where you define which members should be serialized. Here is the similar example for NHibernate.

You can implement some logic which will take only members present in the parent class of dynamic proxy. I hope this will not break lazy loading. To validate that current entity is proxy you can use this code to get all known proxy types:

IEnumerable<Type> types = ((IObjectContextAdapter)dbContext).ObjectContext.GetKnownProxyTypes();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文