Automapper error saying mapper not initialized

2020-02-04 05:21发布

问题:

I am using Automapper 5.2. I have used as a basis this link. I will describe, in steps, the process of setting up Automapper that I went through.

First I added Automapper to Project.json as indicated:

PM> Install-Package AutoMapper

Second I created a folder to hold all files relating to mapping called "Mappings"

Third I set up the configuration of Automapper in its own file in the mappings folder :

public class AutoMapperConfiguration
{
    public MapperConfiguration Configure()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile<ViewModelToDomainMappingProfile>();
            cfg.AddProfile<DomainToViewModelMappingProfile>();
            cfg.AddProfile<BiDirectionalViewModelDomain>();
        });
        return config;
    }
}

Forth Also in its own file in the mappings folder I set up the mapping profile as follows:

public class DomainToViewModelMappingProfile : Profile
{
    public DomainToViewModelMappingProfile()
    {
        CreateMap<Client, ClientViewModel>()
           .ForMember(vm => vm.Creator, map => map.MapFrom(s => s.Creator.Username))
           .ForMember(vm => vm.Jobs, map => map.MapFrom(s => s.Jobs.Select(a => a.ClientId)));
    }
}

Fifth, I added an extension method as its own file also in the mappings folder... this was used next in startup.cs:

public static class CustomMvcServiceCollectionExtensions
{
    public static void AddAutoMapper(this IServiceCollection services)
    {
        if (services == null)
        {
            throw new ArgumentNullException(nameof(services));
        }
        var config = new AutoMapperConfiguration().Configure();
        services.AddSingleton<IMapper>(sp => config.CreateMapper());
    }
}

Sixth I added this line into the ConfigureServices method in Startup.cs

        // Automapper Configuration
        services.AddAutoMapper();

Finally I used it in a controller to convert a collection...

 IEnumerable<ClientViewModel> _clientVM = Mapper.Map<IEnumerable<Client>, IEnumerable<ClientViewModel>>(_clients);

Based on the question I referred to earlier I thought I had everything set so I could use it however I got an error on the line above in the Controller...

"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."

What have I missed.. Is this the correct way to set up Automapper? I suspect I am missing a step here.. any help greatly appreciated.

回答1:

AutoMapper has two usages: dependency injection and the older static for backwards compatibility. You're configuring it for dependency injection, but then attempting to use the static. That's your issue. You just need to choose one method or the other and go with that.

If you want to do it with dependency injection, your controller should take the mapper as a constructor argument saving it to a member, and then you'll need to use that member to do your mapping:

public class FooController : Controller
{
    private readonly IMapper mapper;

    public FooController(IMapper mapper)
    {
        this.mapper = mapper;
    }

Then:

IEnumerable<ClientViewModel> _clientVM = mapper.Map<IEnumerable<Client>, IEnumerable<ClientViewModel>>(_clients);

For the static method, you need to initialize AutoMapper with your config:

public MapperConfiguration Configure()
{
    AutoMapper.Mapper.Initialize(cfg =>
    {
        cfg.AddProfile<ViewModelToDomainMappingProfile>();
        cfg.AddProfile<DomainToViewModelMappingProfile>();
        cfg.AddProfile<BiDirectionalViewModelDomain>();
    });
}

You then need only call this method in Startup.cs; you would no longer register the singleton.



回答2:

Better Solution would be to create extension method like this

  public static DestinationType CastObject<SourceType, DestinationType>(this SourceType obj)
            {
                var config = new MapperConfiguration(cfg =>
                cfg.CreateMap<SourceType, DestinationType>());

                var mapper = config.CreateMapper();

                var entity = mapper.Map<DestinationType>(obj);

                return entity;
            }