如何将动态代理转换为POCO?

时间:2012-07-05 17:51:48

标签: c# asp.net-mvc-3 serialization entity-framework-4.1 dynamic-proxy

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

3 个答案:

答案 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的下一个预览版中。

(因此可能您必须等待以下预览版或最终版本)

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

在.NET 4.5中已修复

根据该问题的评论,如果您使用的是当前版本的JSON.Net,您似乎可以使用NonSerializedAttribute而不是ScriptIgnoreAttribute来解决此问题

答案 2 :(得分:3)

特拉维斯,我知道你已经接受了你的答案,但是想对此进行一些侧面思考。我最近遇到了一个非常类似的问题,无法为我工作,尝试了所有[scriptignore] attibutes等等。

最终对我有用的是使用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);
}

尝试一下,看看你是如何继续前进的,它确实对我有用,'地球'动了:)