Can't access automapper context items after upgrade to 9

Viewed 3279

I have a mapper like this:

CreateMap<Source, ICollection<Dest>>()
    .ConvertUsing((src, dst, context) => 
    {
        return context.Mapper.Map<ICollection<Dest>>
            (new SourceItem[] { src.Item1, src.Item2 ... }.Where(item => SomeFilter(item)),
            opts => opts.Items["SomethingFromSource"] = src.Something);
    });

CreateMap<SourceItem, Dest>()
    .ForMember(d => d.Something, opts => opts.MapFrom((src, dst, dstItem, context)
        => (string)context.Items["SomethingFromSource"]));

This gives me an exception saying You must use a Map overload that takes Action<IMappingOperationOptions>. Well, I do use the Map overload that takes this action. How else can I do this?

2 Answers

This is because of this change:
https://github.com/AutoMapper/AutoMapper/pull/3150

You can get the the Items by accessing the ResolutionContext's Options property.

Change context.Items["SomethingFromSource"] to context.Options.Items["SomethingFromSource"].

When there is no Items, the ResolutionContext is the same with DefaultContext. Therefore The ResolutionContext.Items property will throw the exception.
However, if there is, the ResolutionContext.Items wouldn't be the same with DefaultContext. Therefore the ResolutionContext.Items will return the list.

While ResolutionContext.Options.Items will always return the list, it would not throw any exception, whether it's empty or not. I believe it's what the error message meant, because ResolutionContext.Options is an IMappingOperationOptions

Several points of consideration when you use inner mapper (i.e. context.Mapper)

First, try not to use context.Mapper.Map<TDestination>(...), use context.Mapper.Map<TSource, TDestination>(...) instead, it behaves much better.

Second, use of context in inner mappers will break encapsulation. If you need to set the values in inner objects, consider these two solutions:

In case you want to set the values after the inner map

context.Mapper.Map<Source, Dest> (source, opts => opts.AfterMap((s, d) => 
    d.Something = source.Something))

In case you want to set the values before the inner map

context.Mapper.Map<Source, Dest> (source, new Dest()
    {
        Something = source.Something
    })
Related