Avoid constructor mapping fields

2019-06-19 09:00发布

问题:

I am using AutoMapper 6.2.2 with .NET Core 2.0 and its default dependency injection mechanism to map between models and DTOs. I need DI in my AutoMapper configs because I have to perform an AfterMap<Action> that needs some injected components.

The thing is, for some models that have constructors which parameters match some source member, when I enable DI for AutoMapper (add services.AddAutoMapper()), these constructors are by default called and fed with data, that then breaks my operations with EF.

public class UserDTO
{
    public string Name { get; set; }

    public string Email { get; set; }

    public ICollection<RoleDTO> Roles { get; set; }
}


public class User
{
    public string Name { get; set; }

    public string Email { get; set; }

    public ICollection<RoleInUser> RoleInUsers { get; } = new List<RoleInUser>();

    public ICollection<Role> Roles { get; }

    public User()
    {
        Roles = new JoinCollectionFacade<Role, User, RoleInUser>(this, RoleInUsers);
    }

    public User(string name, string email, ICollection<Role> roles) : this()
    {
        Roles.AddRange(roles);
    }

}

public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserDTO, User>()
            .ForMember(entity => entity.Roles, opt => opt.Ignore())
            .AfterMap<SomeAction>();
    }
}

In the previous snippet, User(name, email, roles) gets called with the list of roles.

My mapper configuration is the following (note the DisableConstructorMapping() option)

    protected override MapperConfiguration CreateConfiguration()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.DisableConstructorMapping();

            // Add all profiles in current assembly
            cfg.AddProfiles(Assemblies);
        });

        return config;
    }

And my Startup where everything is set up:

        var mapperProvider = new MapperProvider();
        services.AddSingleton<IMapper>(mapperProvider.GetMapper());
        services.AddAutoMapper(mapperProvider.Assemblies);

Modifying the profile to configure which ctor to use with ConstructUsing

    public UserProfile()
    {
        CreateMap<UserDTO, User>()
            .ForMember(entity => entity.Roles, opt => opt.Ignore())
            .ConstructUsing(src => new User())
            .AfterMap<SomeAction>();
    }

It works as expected, but this forces me to include this boilerplate statement in every Map configuration, and the model is quite big.

Without dependency injection (this need arosed recently), it worked smoothly with the first snippet (no need for ConstructUsing).

I've searched for this scenario but haven't found anything. Is adding ConstructUsing to every Map the way to go? Is there any better option? Or maybe I'm doing something completely wrong...

回答1:

One year later and I encountered this with AutoMapper 8.0.0. If anyone is still following this, there're 2 ways:

  1. Add ConstructUsing to your every CreateMap<Src, Des>.
  2. Modify/Add to your ConfigureServices this line: services.AddAutoMapper(cfg => cfg.DisableConstructorMapping());

But you have to create a blank constructor in every class needed mapping.