如何让AutoMapper不缓存映射对象?

时间:2012-07-04 18:09:10

标签: c# .net automapper

当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();
    }
}

5 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:8)

我遇到了与mapper相同的问题,环顾四周我找到了解决方案,通过添加

Mapper.Reset();

Source blog(更正的网址)

答案 2 :(得分:4)

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

以下是我的例子:

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的第一个实例(即来源[0])及其所有孩子都很完美

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

  • 标签3的第二个实例(即来源[2])没有任何子女 - 它应该将标签4作为一个孩子

答案 3 :(得分:1)

  

当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(所有内部映射表达式都将丢失)

答案 4 :(得分:0)

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

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

在您的情况下,您有两个共享相同ID的树对象但很明显,它们不是“平等”,因为它们有不同的子项。

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