我正在尝试序列化域模型并遇到需要将动态代理转换为POCO的问题。我遇到的问题是通过模型中的虚拟属性存在循环引用。虽然我试图使用[ScriptIgnore]
以使序列化程序不解析这些属性,但它仍然可以。我认为这是因为对象是动态代理,并且属性中仍然存在一些导致解析器进入的残余(这反过来导致递归错误“循环引用” - 我尝试将递归限制为3步但我得到了“超出递归步骤”的错误。)
如何将对象从动态代理转换为POCO以便可以序列化?
编辑:简单示例
public class One : BaseViewModel
{
public int OneId { get; set; }
public virtual ICollection<Two> Two { get; set; }
}
public class Two
{
public int TwoId { get; set; }
public int OneId { get; set; }
[ScriptIgnore]
public virtual One One { get; set; }
}
public abstract class BaseViewModel
{
public string AsJson()
{
var serializer = new JavaScriptSerializer();
return serializer.Serialize(this);
}
}
答案 0 :(得分:4)
作为Jim的Automapper解决方案的替代方案,我在POCO代理的'Mapping'(浅层复制)到同一POCO的实例方面取得了一些成功。执行此操作的简单方法是修改生成POCO的模板文件以包含ToSerializable()方法,因此POCO类如下所示:
public partial class cfgCountry
{
public cfgCountry()
{
this.People = new HashSet<Person>();
}
[Key]
public int CountryID { get; set; }
public string Name { get; set; }
public int Ordinal { get; set; }
public virtual ICollection<Person> People { get; set; }
public cfgCountry ToSerializable()
{
return new cfgCountry()
{
CountryID = this.CountryID,
Name = this.Name,
Ordinal = this.Ordinal,
};
}
}
这是我添加到POCO模板(tt)文件中的函数,用于创建ToSerializable函数(这种丑陋的语法。):
<#+
void WriteToSerializableMethod (CodeGenerationTools code, IEnumerable<EdmProperty> primitiveProperties, EntityType entity)
{
#>
public <#=code.Escape(entity)#> ToSerializable()
{
return new <#=code.Escape(entity)#>()
{
<#+
foreach(var edmProperty in primitiveProperties)
{
#>
<#=edmProperty.Name#> = this.<#=edmProperty.Name#>,
<#+
}
#>
};
}
<#+
}
#>
这并不完美,因为你需要记住每当你期望结果被序列化时返回foo.ToSerializable()而不是foo,但我希望它对某人有用。
答案 1 :(得分:3)
这是一个已知问题
我们修复了ScriptIgnoreAttribute中的一个问题,该问题未传播到派生类。由于POCO代理类型是通过派生自用户提供的POCO类创建的,因此JavaScriptSerializer无法在您的repro中看到[ScriptIgnore]属性。
此修补程序不会包含在.NET 4.5的下一个预览版中。
(因此可能您必须等待以下预览版或最终版本)
在.NET 4.5中已修复
根据该问题的评论,如果您使用的是当前版本的JSON.Net,您似乎可以使用NonSerializedAttribute而不是ScriptIgnoreAttribute来解决此问题
答案 2 :(得分:3)
最终对我有用的是使用Automapper并创建从代理对象到精简的poco对象的地图。这在2分钟内解决了我的所有问题。所有这一切都是在36小时的围攻精神充沛之后,当试图让代理人打球时,他们占了上风 - - )
在过渡时期考虑的另一种方法。
[编辑] - 使用Automapper(这是一个引用automapper的小测试应用)
参考:http://automapper.codeplex.com/
nuget:Install-Package AutoMapper
类:
public sealed class One : BaseViewModel
{
// init collection in ctor as not using EF in test
// no requirement in real app
public One()
{
Two = new Collection<Two>();
}
public int OneId { get; set; }
public ICollection<Two> Two { get; set; }
}
public class Two
{
public int TwoId { get; set; }
public int OneId { get; set; }
[ScriptIgnore]
public virtual One One { get; set; }
}
public abstract class BaseViewModel
{
public string AsJson()
{
var serializer = new JavaScriptSerializer();
return serializer.Serialize(this);
}
}
public class OnePoco : BaseViewModel
{
public int OneId { get; set; }
public virtual ICollection<TwoPoco> Two { get; set; }
}
public class TwoPoco
{
public int TwoId { get; set; }
public int OneId { get; set; }
}
测试控制器代码:
public ActionResult Index()
{
// pretend this is your base proxy object
One oneProxy = new One { OneId = 1 };
// add a few collection items
oneProxy.Two.Add(new Two() { OneId = 1, TwoId = 1, One = oneProxy});
oneProxy.Two.Add(new Two() { OneId = 1, TwoId = 2, One = oneProxy});
// create a mapping (this should go in either global.asax
// or in an app_start class)
AutoMapper.Mapper.CreateMap<One, OnePoco>();
AutoMapper.Mapper.CreateMap<Two, TwoPoco>();
// do the mapping- bingo, check out the asjson now
// i.e. oneMapped.AsJson
var oneMapped = AutoMapper.Mapper.Map<One, OnePoco>(oneProxy);
return View(oneMapped);
}
尝试一下,看看你是如何继续前进的,它确实对我有用,'地球'动了:)