7

我试图序列化域模型并遇到了需要将动态代理转换为 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);
    }
}
4

3 回答 3

4

作为 Jim 的 Automapper 解决方案的替代方案,我在将 POCO 代理“映射”(浅复制)到同一个 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 本身,但我希望它对某人有用。

于 2012-12-20T16:24:55.583 回答
3

这是一个已知的问题

我们修复了 ScriptIgnoreAttribute 中的一个问题,该问题未传播到派生类。由于 POCO 代理类型是通过派生自用户提供的 POCO 类创建的,因此 JavaScriptSerializer 无法看到您在 repro 中拥有的 [ScriptIgnore] 属性。

该修复不会包含在 .NET 4.5 的下一个预览版中。

(所以大概你必须等待下一个预览版本或最终版本)

http://connect.microsoft.com/VisualStudio/feedback/details/723060/ef-4-2-code-first-property-attributes-not-honoured

在 .NET 4.5 中已修复

从对该问题的评论来看,如果您使用的是 JSON.Net 的当前版本,您可以使用 NonSerializedAttribute 而不是 ScriptIgnoreAttribute 来解决

于 2012-07-05T18:17:22.037 回答
3

特拉维斯,我知道你在这里有你接受的答案,但想就此进行一点横向思考。我最近遇到了一个非常相似的问题,无法为我工作,尝试了所有的 [scriptignore] 属性等,等等。

最终对我有用的是使用 Automapper 并创建从代理对象到精简的 poco 对象的映射。这在 2 分钟内解决了我所有的问题。在试图让代理打球时,经过 36 小时的围攻心态占了上风 - 嘘 :-)

在此期间要考虑的另一种方法。

[编辑] - 使用 Automapper(这是一个引用 automapper 的小测试应用程序)

参考:http ://automapper.codeplex.com/

nuget:安装包 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);
}

试一试,看看你的进展如何,它确实对我有用,“地球”动了:)

于 2012-07-05T20:01:41.507 回答