Can I disable AutoMapper reference cache?
Asked Answered
T

1

10

I believe AutoMapper is using a cache when it is mapping lists of objects from one type to another. Take a look at the following code:

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Mapper.CreateMap<Source, Destination>()
                       .ForMember(dest => dest.SomeOtherNumber, opt => opt.Ignore());

            Mapper.AssertConfigurationIsValid();

            var sourceList = new List<Source>();

            var s1 = new Source {SomeNumber = 10};
            var s2 = new Source {SomeNumber = 69};

            sourceList.Add(s1);
            sourceList.Add(s1);
            sourceList.Add(s1);
            sourceList.Add(s2);

            var destList = Mapper.Map<List<Source>, List<Destination>>(sourceList);

            destList[0].SomeOtherNumber = 100;

            destList.ForEach(x => Console.WriteLine("SomeNumber: {0}, SomeOtherNumber: {1}", x.SomeNumber, x.SomeOtherNumber));
            Console.ReadLine();
        }
    }


    public class Source
    {
        public int SomeNumber { get; set; }
    }

    public class Destination
    {
        public int SomeNumber { get; set; }
        public int SomeOtherNumber { get; set; }
    }

}

The output of the code shows that even though I set only the SomeOtherNumber of the first object in the destList list, the first three items have the same property value because they are referencing the same object reference. I would like to know if I can disable this behavior, so even if I have duplicate references in the source list, I will not have duplicate references in the destination list. Does this make sense?

Here's the ouput:

SomeNumber: 10, SomeOtherNumber: 100
SomeNumber: 10, SomeOtherNumber: 100
SomeNumber: 10, SomeOtherNumber: 100
SomeNumber: 69, SomeOtherNumber: 0
Tudor answered 22/1, 2013 at 18:42 Comment(3)
AutoMapper is a decent utility; however, we've switched to using Value Injecter. Much easier to use IMO, no configuration/setup and resolves the sharing of references you're seeing with AutoMapper OOB.Athal
I appreciate the comment. I have actually seen Value Injecter mentioned when I was trying to figure this out from other SO questions. I may end up using that if I cannot figure this out.Tudor
If you change Destination to a struct instead of class it works correctly. What is the downside of this?Catherine
L
5

I would say this is cannot be done with AutoMapper as the time being. The instance caching is built in pretty deep and currently it can't be turned off easily. Altough your use case is very strange... having the same source object multiple times in a list but you still want different destination objects is quite unusual in my point of view.

But it can be worked around with a pretty bad hack, because it will turn off instance caching globally and relies on the implementation details of AutoMapper so use it carefully. And if you still want to try it out the below mentioned solution is not working with Automapper 2.2.0 because of a bug which is only fixed in the development branch so you need to build AutoMapper from source or wait for the next release.

But nevertheless here are the details

The instance caching is handled by the TypeMapObjectMapperRegistry.CacheMappingStrategy [source] which like all the other ITypeMapObjectMapper implementation is a private class.

However the TypeMapObjectMapperRegistry.AllMappers field which should return all the ITypeMapObjectMapper is a public Func which can be overridden:

So add this code before the first usage of the Mapper class:

var baseMappers = TypeMapObjectMapperRegistry.AllMappers();
TypeMapObjectMapperRegistry.AllMappers = () => 
    baseMappers.Where(m => m.GetType().Name != "CacheMappingStrategy").ToArray();

Mapper.CreateMap<Source, Destination>();
Layla answered 22/1, 2013 at 20:8 Comment(4)
Ok, I appreciate the information. I agree the use case might seem strange, but I feel like it's a bit presumptuous to think that the default behavior is always correct.Tudor
Pretty funny bug :) Almost two years later and the problem still exists. The good question now is what are the consequences of disabling the cache strategy.Felicle
does Mapper.Reset() help?Goraud
Mapper.Reset() could help but it will also reset all your mappings so you have to call all your Mapper.CreateMaps again. So probably it is better to remove the CacheMappingStrategy by hand.Layla

© 2022 - 2024 — McMap. All rights reserved.