Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid constructor mapping fields

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

like image 565
keycad Avatar asked Feb 14 '18 14:02

keycad


1 Answers

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.

like image 149
Củ Su Hào Avatar answered Sep 23 '22 02:09

Củ Su Hào