AutoMapper/AutoMapper.Collection

When mapping Lists, if the DestinationType is added to a DataContext, automapper fails with an exception

k290 opened this issue · 1 comments

k290 commented

Source/destination types

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

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

Mapping configuration

   CreateMap<SourceType, DestinationType>()

Version: 10.1.1

Expected behavior

When mapping lists, exception is not thrown if the DestinationType happens to be added as a DbSet to the DbContext

Actual behavior

Exception is thrown

Steps to reproduce

Consider the following code mapping a List from the SourceType to the DestinationType:

var result= Mapper.Map<List<SourceType>, List<DestinationType>>(inputSource);

This works perfectly fine.

However when DestinationType is added to a DataContext as such

public class DataContext : BaseDbContext, INotificationDataContext
    {
        public DataContext(DbContextOptions<DataContext> options, IServiceProvider services) : base(options, services)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

        }

        public DbSet<DestinationType> DestinationTypes{ get; set; }
    }
    

Then automapper fails with the below exception and stack trace when doing the mapping:
Object reference not set to an instance of an object.
Stack Trace:
at AutoMapper.Collection.Runtime.GeneratePropertyMapsFeature.<>c.b__5_0(PropertyMap pm)
at System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 predicate)
at AutoMapper.Collection.Runtime.GeneratePropertyMapsFeature.CreateEquivalentExpression(IEnumerable1 propertyMaps) at AutoMapper.Collection.Runtime.GeneratePropertyMapsFeature.<>c__DisplayClass3_0.<Get>b__1(IGeneratePropertyMaps x) at System.Linq.Enumerable.SelectIListIterator2.MoveNext()
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable1 source, Func2 predicate, Boolean& found)
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1 source, Func2 predicate)
at AutoMapper.Collection.Runtime.GeneratePropertyMapsFeature.<>c__DisplayClass3_0.b__0(TypePair _)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at AutoMapper.Collection.Runtime.GeneratePropertyMapsFeature.Get(TypeMap typeMap)
at AutoMapper.EquivalencyExpression.EquivalentExpressions.GetEquivalentExpression(IConfigurationProvider configurationProvider, TypeMap typeMap)
at AutoMapper.EquivalencyExpression.EquivalentExpressions.GetEquivalentExpression(IConfigurationObjectMapper mapper, Type sourceType, Type destinationType)
at AutoMapper.Mappers.EquivalentExpressionAddRemoveCollectionMapper.MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap, Expression sourceExpression, Expression destExpression, Expression contextExpression)
at AutoMapper.MapperConfiguration.GenerateObjectMapperExpression(MapRequest mapRequest, IObjectMapper mapperToUse)
at AutoMapper.MapperConfiguration.BuildExecutionPlan(MapRequest mapRequest)
at AutoMapper.MapperConfiguration.CompileExecutionPlan(MapRequest mapRequest)
at AutoMapper.Internal.LockingConcurrentDictionary2.<>c__DisplayClass2_1.<.ctor>b__1() at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) at System.Lazy1.CreateValue()
at System.Lazy1.get_Value() at AutoMapper.Internal.LockingConcurrentDictionary2.GetOrAdd(TKey key)
at AutoMapper.MapperConfiguration.GetExecutionPlan(MapRequest mapRequest)
at AutoMapper.MapperConfiguration.GetExecutionPlan[TSource,TDestination](MapRequest mapRequest)
at AutoMapper.Mapper.MapCore[TSource,TDestination](TSource source, TDestination destination, ResolutionContext context, Type sourceType, Type destinationType, IMemberMap memberMap)
at AutoMapper.Mapper.Map[TSource,TDestination](TSource source, TDestination destination)
at AutoMapper.Mapper.Map[TSource,TDestination](TSource source)

Note that if the DbSet is removed from the dbContext the mapping works perfectly fine. Also note that this is only an issue when mapping lists

This looks to be an issue with Equivalency expression when mapping types using AM.Collection.EF or EFCore.
It looks at DestinationType to find Primary Key members and used maps with said key properties to generate an equivalency expression. This expression fails with these errors when no matches are present.

This is so when you want to update an existing item, or compare classes to each other you know which ones already exist in the database and can be updated, and which should be removed or deleted. You can also add EquivalencyExpression() after create map to make your own comparison expression and that will fix the error message as well.