简体   繁体   中英

Automapper custom resolver using inline map causes tests to fail

Got a mapper configuration which includes:

this.CreateMap<MyProp1, MyDesintationProp>();

this.CreateMap<MyProp2, MyDesintationProp>();

Then, inside the mapper for a more complex object, both these are used inside an inline ResolveUsing to merge into a list of MyDestinationProp:

.ForMember(dest => dest.MyDesintationPropList, opt => opt.ResolveUsing(tc =>
{
    var results = new List<MyDesintationProp>();
    
    if (tc.MyProp1 != null)
    {
        results.Add(Mapper.Map<MyDestinationProp>(tc.MyProp1));
    }

    if (tc.MyProp2 != null)
    {
        results.Add(Mapper.Map<MyDestinationProp>(tc.MyProp2));
    }

    return results;
}))

This works fine in production, but causes tests on this mapping to blow up, complaining

Property: MyDestinationProp ---- System.InvalidOperationException : Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.

Which I guess makes sense because the mapper inside the mapper hasn't been initialised. But what's the best way to go about fixing this?

This is a very trivial example, that really doesn't need to use the context, cause it would also work with simple mappings.

public class Source
{
    public string Name { get; set; }
    public SourceChild Child { get; set; }
    public override string ToString() => $"{typeof(Source)} -> {Name}";
}

public class Destination
{
    public string Name { get; set; }
    public DestinationChild Child { get; set; }
    public override string ToString() => $"{typeof(Destination)} -> {Name}";
}

public class SourceChild
{
    public string Name { get; set; }
    public override string ToString() => $"{typeof(SourceChild)} -> {Name}";
}

public class DestinationChild
{
    public string Name { get; set; }
    public override string ToString() => $"{typeof(DestinationChild)} -> {Name}";
}

public class MappingContextProfile : Profile
{
    public MappingContextProfile()
    {
        CreateMap<Source, Destination>()
            .ForMember(source => source.Child, conf => conf.MapFrom((source, destination, destinationChild, context) =>
            {
                // Not really needed in this case, cause it does just some simple mapping.
                // But if you would need to add some informations from outside
                // (e.g. some interface mapping, etc)
                // this would be the place without losing the magic of AutoMapper.
                return context.Mapper.Map<DestinationChild>(source.Child);
            }));

        CreateMap<SourceChild, DestinationChild>();
    }
}

public class MappingSimpleProfile : Profile
{
    public MappingSimpleProfile()
    {
        CreateMap<Source, Destination>();
        CreateMap<SourceChild, DestinationChild>();
    }
}


public static class Program
{
    public static async Task<int> Main(string[] args)
    {
        var config = new MapperConfiguration(cfg => cfg.AddProfile<MappingContextProfile>());
        var mapper = config.CreateMapper();

        var sources = Enumerable.Range(1, 10)
            .Select(i => new Source { Name = $"{i}", Child = new SourceChild { Name = $"{i * 100}" } })
            .ToList();

        var destinations = mapper.Map<List<Destination>>(sources);

        foreach (var item in destinations)
        {
            Console.WriteLine($"{item} -> {item.Child}");
        }

        return 0;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM