问题
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();
}
}
回答1:
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);
回答2:
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)
回答3:
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
回答4:
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)
回答5:
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.
来源:https://stackoverflow.com/questions/11333732/how-do-i-get-automapper-to-not-cache-mapped-objects