我正在使用MVC3,Razor视图引擎,带有工作单元的存储库模式以及使用EF4.1 Code First来构建应用程序来定义我的数据模型。
这里有一些背景(如果你愿意,还可以使用它)。
应用程序本身只是一个Intranet“菜单”。
2个主要实体是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 Serializer,因为这似乎是循环引用问题的一个很好的方法。我现在不确定如何“修复”代理生成问题。目前,序列化程序抛出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序列化程序的Custom 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实体对象实现的接口。
答案 0 :(得分: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属性。
答案 1 :(得分:0)
好吧,你使用了强大的序列化API,它也会引用参考文献和所有成员,现在你抱怨它序列化了所有成员:)
我没有测试它,但我相信这会让你接近解决方案。
JSON.NET是一个非常强大的工具,它应该为您提供可扩展点以避免这种行为,但您必须自己编写代码。您将需要自定义DataContractResolver
,您可以在其中定义应序列化的成员。 Here是NHibernate的类似示例。
您可以实现一些逻辑,该逻辑仅包含动态代理的父类中的成员。我希望这不会破坏延迟加载。要验证当前实体是否为代理,您可以使用此代码获取所有已知代理类型:
IEnumerable<Type> types = ((IObjectContextAdapter)dbContext).ObjectContext.GetKnownProxyTypes();