【问题标题】:How do I get AutoMapper to not cache mapped objects?如何让 AutoMapper 不缓存映射对象?
【发布时间】:2012-07-05 05:34:01
【问题描述】:

当 AutoMapper 遇到一个已经被映射的对象时,它似乎会再次使用该对象,而不是尝试重新映射它。我相信它是基于.Equals() 做到这一点的。

我有一棵正在被映射的树。因此,一个具有一些属性和子节点的节点。多个节点具有相同的 .Equals() 值,因为它基于 Id 属性。节点的子节点不同,我需要重新映射这些节点,但它使用的是缓存的映射值。

有没有办法关闭缓存映射?我能想到的只是实现一个新的转换器,但这完全违背了使用 AutoMapper 的目的。

这是一个如何复制的示例。

void Main()
{
    var source = new List<Tag>
    {
        new Tag 
        { 
            Id = 1, 
            Name = "Tag 1", 
            ChildTags = new List<Tag>
            {
                new Tag 
                { 
                    Id = 2, 
                    Name = "Tag 2", 
                    ChildTags = new List<Tag> 
                    {
                        new Tag {Id = 3, Name = "Tag 3"},
                        new Tag {Id = 4, Name = "Tag 4"}
                    }
                }
            }
        },
        new Tag { Id = 1, Name = "Tag 1" },
        new Tag 
        {
            Id = 3, Name = "Tag 3", ChildTags = new List<Tag>
            {
                new Tag {Id = 4, Name = "Tag 4"}
            }
        }
    };

    Mapper.CreateMap<Tag, Tag>();
    var results = Mapper.Map<IList<Tag>, IList<Tag>>(source);

    results.Dump();
}

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Tag> ChildTags { get; set; }

    public override bool Equals(Object obj)
    {
        if (obj == null)
        {
            return false;
        }

        var x = this;
        var y = (Tag)obj;

        return x.Id.Equals(y.Id);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

【问题讨论】:

  • 我会尝试自己实例化 MappingEngine 并像上面所说的那样控制生命周期,看看是否能做到这一点。
  • 所以,看起来我需要实现一个 ITypeMapFactory。这对我需要做的事情来说太多了。如果有一种简单的方法可以将其关闭,那就太好了,但看起来情况并非如此。如果您想创建此问题的答案并放入示例,我会将其标记为您的答案。
  • 当您说“遇到一个已经被映射的对象”时,您是指映射配置(Mapper.CreateMap)还是实际映射(Mapper.Map)? Automapper 不缓存以前映射的实例(即不缓存 Mapper.Map 调用结果)。
  • 将“等于”更改为不将具有相同 id 的项目视为同一对象是否有任何价值? (即,如果您的其余代码不依赖于它以某种方式工作,似乎它会修复它?)

标签: c# .net automapper


【解决方案1】:

现在有一个禁用缓存的选项。

Mapper.CreateMap<Tag, Tag>();
var results = Mapper.Map<IList<Tag>, IList<Tag>>(source, opt => opt.DisableCache = true);

【讨论】:

    【解决方案2】:

    我在映射器上遇到了同样的问题,环顾四周,我发现了一个解决方案,通过添加

    Mapper.Reset();
    

    Source blog(更正网址)

    【讨论】:

    • 谢谢你,这让我发疯了!
    • 这在树场景中不起作用。如果您查看 Saan 的示例并覆盖 GetHashCode,您会看到它发生了。我不确定你可以在哪里注入Mapper.Reset()
    • 重置应该在通话前后进行。
    • 如何在 Automapper 5.0 中做到这一点?
    【解决方案3】:

    我也遇到了同样的问题。 当您映射同一个对象两次时不会发生这种情况 - 当您具有对象的树继承关系时会发生这种情况,并且树的两个位置存在相同的值(但具有不同的子值) 映射项目的第二个实例时 - 它使用第一个实例的子值,而不是重新评估子值应该是什么。

    这是我的例子:

    class Tag { 
      int Id {get; set;}
      string Name {get; set;}
      IEnumerable<Tag> ChildTags  {get; set;}
    }
    
    public void Test()
    {
    var source =  new List<Tag>
                {
                    new Tag { Id = 1, Name = "Tag 1", ChildTags = new List<Tag>
                                {
                                    new Tag { Id = 2, Name = "Tag 2", ChildTags = new List<Tag> 
                                                {
                                                    new Tag {Id = 3, Name = "Tag 3"},
                                                    new Tag {Id = 4, Name = "Tag 4"}
                                                }
                                        }
                                }
                        },
                    new Tag { Id = 1, Name = "Tag 1" },
                    new Tag {
                            Id = 3, Name = "Tag 3", ChildTags = new List<Tag>
                                {
                                    new Tag {Id = 4, Name = "Tag 4"}
                                }
                        }
                };
    
    Mapper.CreateMap<Tag, Tag>()
        .ForMember(dest => dest.ChildTags,
            opt => opt.MapFrom(src => src.ChildTags));
    var result = Mapper.Map<IList<Tag>, IList<Tag>>(tags);
    }
    

    在结果中

    • 标签 1 的第一个实例(即 source[0])及其所有子对象都是完美的

    • 标签 1 的第二个实例(即 source[1])具有第一个实例的所有子级 - 它不应该有任何子级

    • 标签 3 的第二个实例(即 source[2])没有任何子级 - 它应该有标签 4 作为子级

    【讨论】:

      【解决方案4】:

      当 AutoMapper 遇到一个已经被映射的对象时,它 似乎再次使用该对象,而不是尝试重新映射它。一世 相信它是基于 .Equals()

      您能解释一下为什么以及何时看到吗?

      快速查看源代码后,我确定没有对象缓存。 这是一个说明这一点的测试:

         public class CustomerSource
          {
              public string FirstName { get; set; }
              public string LastName { get; set; }
              public DateTime DateOfBirth { get; set; }
      
              public int NumberOfOrders { get; set; }
          }
      
          public class CustomerTarget
          {
              public string FirstName { get; set; }
              public string LastName { get; set; }
              public DateTime DateOfBirth { get; set; }
      
              public int NumberOfOrders { get; set; }
          }
      
          [TestMethod]
          public void Test_AutoMapper()
          {
              Mapper.CreateMap<CustomerSource, CustomerTarget>();
      
              var source = new CustomerSource() { DateOfBirth = DateTime.Now, FirstName = "FirstName", LastName = "LastName", NumberOfOrders = int.MaxValue };
      
              var res1 = Mapper.Map<CustomerSource, CustomerTarget>(source);
              Console.WriteLine(res1.FirstName); // PRINT FirstName
      
              source.FirstName += "[UPDATED]";
              source.LastName += "[UPDATED]";
      
              var res2 = Mapper.Map<CustomerSource, CustomerTarget>(source);
              Console.WriteLine(res1.FirstName); // PRINT FirstName[UPDATED]
      
          }
      

      没有您的代码,很难深入。 还有一个方法 Mapper.Reset() 清除 MapperEngine 和 MapingConfiguration (所有内部映射表达式都会丢失)

      【讨论】:

      • 对不起,我是说他的答案有代码显示问题的重现。我用引号说“答案”,因为它不是答案,而是评论。
      • 另外,库创建者说它被缓存了。 groups.google.com/forum/?fromgroups#!topic/automapper-users/…现在已经好几年了,也许这是已经“修复”的东西了。
      【解决方案5】:

      您正在映射的树对象的 Equals 行为似乎不合适。

      只有当“指定对象等于当前对象”时,该方法才应返回 true。 - http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

      在您的情况下,您有两个树对象共享相同的 id,但显然,它们不“相等”,因为它们有不同的子对象。

      我建议看看为什么 Equals 方法会以这种方式被滥用,以及是否可以通过不覆盖 Equals 方法来获得所需的行为,而是使用不同的方法来检查具有更合适名称的树 id 字段,例如. TreeIdsAreEqual。

      【讨论】:

      • 什么使对象相等?在数据库中,它是主键。这意味着所有其他字段都可能不同,即使我们知道它代表相同的事物。
      猜你喜欢
      • 1970-01-01
      • 2017-07-20
      • 1970-01-01
      • 2021-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-16
      相关资源
      最近更新 更多