【问题标题】:Automapper Circular Reference Infinite loopsAutomapper 循环参考无限循环
【发布时间】:2018-10-01 02:42:03
【问题描述】:

我在使用 AutoMapper 时遇到了一些问题,我映射的对象进行循环引用,因此我无法使用 ActionResult 将其 JSON 返回给 View。

我已经将一个 DTO 的对象与另外两个链接在一起。

 public class MasterJobsDTO
{
    public int function_id { get; set; }
    public string function_name { get; set; }
    public bool is_active { get; set; }
    public job_family job_family
    {
        get; set;

    }
    public functional_area functional_area
    {
        get; set;

    }
}

功能模式:

 public partial class function
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public function()
    {
        this.t_actual_organization = new HashSet<t_actual_organization>();
        this.t_actual_organization_split_position = new HashSet<t_actual_organization_split_position>();
    }

    public int function_id { get; set; }
    public string function_name { get; set; }
    public bool is_active { get; set; }
    public Nullable<int> job_family_id { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<t_actual_organization> t_actual_organization { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<t_actual_organization_split_position> t_actual_organization_split_position { get; set; }
    public virtual job_family job_family { get; set; }
}

Job_Family 模型:

public partial class job_family
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public job_family()
    {
        this.t_actual_organization = new HashSet<t_actual_organization>();
        this.t_actual_organization_split_position = new HashSet<t_actual_organization_split_position>();
        this.functions = new HashSet<function>();
    }

    public int job_family_id { get; set; }
    public string job_family_name { get; set; }
    public Nullable<int> functional_area_id { get; set; }

    public virtual functional_area functional_area { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<t_actual_organization> t_actual_organization { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<t_actual_organization_split_position> t_actual_organization_split_position { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<function> functions { get; set; }
}

自动映射器配置:

cfg.CreateMap<function, MasterJobsDTO>().MaxDepth(1).PreserveReferences()
        .ForMember(x => x.functional_area_id, opts => opts.MapFrom(source => source.job_family.functional_area.functional_area_id))
        .ForMember(x => x.functional_area_extended_name, opts => opts.MapFrom(source => source.job_family.functional_area.functional_area_extended_name))
        .ForMember(x => x.job_family_name, opts => opts.MapFrom(source => source.job_family.job_family_name))
        .ForMember(x => x.functional_area, opts => opts.MapFrom(source => source.job_family.functional_area))
        ;

function_area 类:

 public partial class functional_area
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public functional_area()
    {
        this.job_family = new HashSet<job_family>();
        this.t_actual_organization = new HashSet<t_actual_organization>();
        this.t_actual_organization_split_position = new HashSet<t_actual_organization_split_position>();
    }

    public int functional_area_id { get; set; }
    public string functional_area_name { get; set; }
    public string functional_area_extended_name { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<job_family> job_family { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<t_actual_organization> t_actual_organization { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<t_actual_organization_split_position> t_actual_organization_split_position { get; set; }
}

还有电话:

List<MasterJobsDTO> mjd = Mapper.Map<List<function>, List<MasterJobsDTO>>(data);

我在浏览器中得到的错误是:

在序列化“System.Data.Entity.DynamicProxies.job_family_D3FE2013BDB6002B7BE94915E73AEA531401...”类型的对象时检测到循环引用...

谢谢!

【问题讨论】:

    标签: c# .net api entity automapper


    【解决方案1】:

    在您的自动映射器配置中,您可以排除有问题的循环引用指向。

    .ForMember(dest => dest.OffendingVariable, source=> source.Ignore());
    

    自动映射器完成后得到的对象将比“实体”对象“小”,并且可以毫无问题地序列化为 JSON。

    编辑:如果您的真正错误在于您最终希望能够将“无限”对象序列化为 JSON,并且您不关心通过摆弄 automapper 来修复它,我可以建议“裁剪”循环用这样的东西指向你的对象的背面:

    List<MasterJobsDTO> mjd = Mapper.Map<List<function>, List<MasterJobsDTO>>(data);
    
    var jsonPrepMJD = new List<MasterJobsDTO>(from m in mjd
                                select new MasterJobsDTO()
                                {
                                  id = m.id,
                                  ...,
                                  pointBackMember = new PointBackMember(){set all but the virtual pointback}
                                }.Cast<MasterJobsDTO>();
    

    如果 pointBackMember 是一个列表,则从中进行选择并将其投射到你需要的深度

    jsonPrepMJD 然后就可以序列化了。

    【讨论】:

    • 如果我忽略 MasterJobDTO 中的 job_family 属性,它将为空。我想从 JavaScript job_family 属性访问(例如 masterJobDTO.job_family.job_family_name),然后我想将 masterjob 的对象发送回服务器,可能带有新值。
    • 再次编辑建议手动裁剪出虚拟点,虽然感觉很脏:)
    • 公共虚拟 ICollection 函数 { get;放; } 链接到类 Function 并在序列化时尝试将其树添加到节点。类函数有 public virtual job_family job_family { get;放; } 这是你的循环开始的地方
    • 你说用 function_area = new function_area(){} 替换 pointBackMember = new PointBackMember() ?
    • pointbacks 的问题是它们被用来驱动从它自己的角度组织一个对象。在循环指向中,您得到的“真实地图”树会根据您从哪个角度开始而有所不同。我认为您可能必须“硬”定义自己没有两种方法
    【解决方案2】:

    等等,是 automapper 的问题还是 json 格式化程序无法处理循环引用 (CR)?

    如果是 json,你可以配置你的 api 来处理 CR。 Here 是一个关于如何让它忽略 CR 的过度学术示例的链接。 Here 是设置选项。我能够在我的 WebApiConfig.cs 中全局解决问题

    就我个人而言,我宁愿让 json 能够正确表示数据,也不愿改变我的编码实践,因为我只能深入 X 级。

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
            //this will ignore
            json.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            //this will serialize them to objects.
            json.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
            json.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
         }
    }
    

    我在使用新版本的 automapper 时遇到了类似的问题。 Automapper 应该能够statically figure out the CRs in 6.1+,但我有一个非常复杂的 dto 模型,其中包含许多 CR。我正在等待自动映射器团队解决我的问题。在此期间,我恢复到 4.2.1.0 并且一切正常。在我解决了自动映射器异常后,我从 json 格式化程序中得到了一个异常,上面的忽略配置解决了我的问题。

    Here 让我在 json 问题上走上了正轨。

    【讨论】:

    • 没有关于循环引用的已知 AM 问题。使用错误的可能性更大。
    • 如果您阅读 OP 和我的帖子,而不是复制粘贴您在我的其他帖子上发表的相同评论。您会看到我的解决方案是用于解析 JSON 而不是 AM 中的循环引用。但感谢两者的反对。
    • 我特别反对 AM 内容。删除它,我会撤消。
    • 您为什么不赞成还原选项?它不提供另一种解决方案吗?它不能解决问题吗?我特别说它应该在最新版本中工作,甚至提供文档链接,说明如何实施 AM 批准的解决方案,如果没有......即使是 AM 团队也会考虑到它可能无法工作的情况。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-25
    • 1970-01-01
    相关资源
    最近更新 更多