-
Notifications
You must be signed in to change notification settings - Fork 63
Description
I am seeing the following NullReferenceException thrown from my web service application:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at ExpressMapper.MappingServiceProvider.CompileNonGenericCustomTypeMapper(Type srcType, Type dstType, ICustomTypeMapper typeMapper, Int64 cacheKey)
at ExpressMapper.MappingServiceProvider.MapNonGenericInternal(Type srcType, Type dstType, Object src, Object dest, Boolean dynamicTrial)
at ExpressMapper.MappingServiceProvider.Map[T,TN](T src)
...
I believe that the root cause is a race condition that gets exercised in the following scenario.
Let's say I have the following classes and interface
public interface IFoo { … }
public class CoreFoo : IFoo { … }
public class ApiFoo { … }
And a custom mapping registration like
Mapper.RegisterCustom<IFoo, ApiFoo>(…);
Which I use with the following call to Map
var foo = new CoreFoo(…);
return foo.Map<IFoo, ApiFoo>();
Since the runtime type of foo is not IFoo (it's CoreFoo), my call to Map method will call into MapNonGenericInternal. The non-generic method finds my custom registration for IFoo -> ApiFoo and attempts to compile and cache an Expression for invoking the custom mapping in this block. The cache it uses is a Dictionary, which is not thread safe under modification. So, here we have a possible race condition wherein multiple threads have a cache miss for the mapping above, attempt to Insert the newly compiled Expression to the cache, and one thread fails as the Dictionary has already been modified by some other thread. As best as I can tell, a NullReferenceException is a possibility when two threads attempt to modify a Dictionary concurrently.
I can work around the problem on my end by changing my registration or synchronizing this particular call to Map. For a long term solution, is it possible to make the usage of this cache thread safe from your end? Say with a ConcurrentDictionary? Let me know if you need more information.