简体   繁体   中英

How do I get AutoMapper to not cache mapped objects?

When AutoMapper encounters an object that's already been mapped, it seems to use that object again, instead of trying to re-map it. I believe it does this based on .Equals() .

I have a tree that's being mapped. So, a node with some properties, and children. More than one of the nodes have the same value of .Equals() , because it's based off an Id property. The children of the nodes are different and I need those re-mapped, but it's using a cached map value.

Is there a way to turn the cached mapping off? All I can think of is implementing a new converter, but that totally defeats the purpose of using AutoMapper.

Here is an example on how to reproduce.

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

There is now an option to disable the cache.

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

I've faced the same issue with the mapper, looking around i found that a solution for it, by adding

Mapper.Reset();

Source blog (corrected URL)

I also get the same issue. It doesn't happen when you map the same object twice - it happens when you have a tree heirarcy of objects, and the same value exists in two places of the tree (but with different child values) When mapping the second instance of the item - it uses the child values of the first instance, instead of re-evaluating what the child values should be.

Here is my example:

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

In the result

  • the first instance of Tag 1 (ie source[0]) and all of its children are perfect

  • the second instance of Tag 1 (ie source[1]) has all the children of the first instance - it should not have any children

  • the second instance of Tag 3 (ie source[2]) does not have any children - it should have Tag 4 as a child

When AutoMapper encounters an object that's already been mapped, it seems to use that object again, instead of trying to re-map it. I believe it does this based on .Equals()

Can you explain why and when you see that ?

After a quick look in the source code, I'm sure there is no cache for objects. Here is a test that illustrate this :

   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]

    }

Without your code, it is difficult to go more deeply. There is also a method Mapper.Reset() that clears the MapperEngine and the MapingConfiguration (all internal mapping expressions will be lost)

It seems that the Equals behaviour of the tree object you're mapping is inappropriate.

The method should only return true if "the specified object is equal to the current object." - http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

In your case, you have two tree objects sharing the same id but clearly, they are not "equal" since they have different children.

I suggest looking at why the Equals method has been abused in this way and whether you could get the behaviour you need by not overriding the Equals method, instead using a different method to check the tree id field with a more appropriate name eg. TreeIdsAreEqual.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM