Search code examples
c#automapperautomapper-6

Why does Automapper map Driver.Name to DriverName automatically


I have the following classes:

public partial class ScheduledDeduction
{
    public int ID { get; set; }

    public int DriverId { get; set; }

    public string Description { get; set; }

    public DateTime DateTime { get; set; }

    public decimal Amount { get; set; }

    public virtual Driver Driver { get; set; }
}

public partial class Driver
{

    public int ID { get; set; }

    public int CompanyId { get; set; }

    public string Name { get; set; }

and View Model class:

public abstract class ScheduledDeductionDetailVM
{
    public int ID { get; set; }

    [Display(Name = "Driver Name")]
    public string DriverName { get; set; }

    public string Description { get; set; }

    [Display(Name = "Date")]
    [DisplayFormat(DataFormatString = "{0:d}")]
    public System.DateTime DateTime { get; set; }

    [Display(Name = "Amount")]
    [DisplayFormat(DataFormatString = "{0:c}")]
    public decimal Amount { get; set; }
}

I have the following rule of Automapper:

CreateMap<Infrastructure.Asset.ScheduledDeduction, ViewModels.ScheduledDeductionDetailVM>();

and try to use:

ScheduledDeduction scheduledDeduction = db.ScheduledDeductions.Find(id);
ScheduledDeductionDetailVM model = mapper.Map<ScheduledDeductionDetailVM>(scheduledDeduction);

and it works! Why? ScheduledDeductionDetailVM has property DriverName , which can be got from ScheduledDeduction.Driver.Name and it never described at all. But it's mapped correctly...


Solution

  • Automapper uses many different conventions, and this behaviour is also part of different conventions. On the page about Flattening it is stated that

    If for any property on the destination type a property, method, or a method prefixed with "Get" does not exist on the source type, AutoMapper splits the destination member name into individual words (by PascalCase conventions).

    But if you do this:

    var config = new MapperConfiguration(cfg => {
        cfg.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();                
        cfg.CreateMap<ScheduledDeduction, ScheduledDeductionDetailVM>();
    });
    

    It will no longer be the case, because you changed destination member naming convention from PascalCase to LowerUnderscore.

    Or if you do this:

    var config = new MapperConfiguration(cfg => {
        var profile = (Profile)cfg;
        profile.DefaultMemberConfig.MemberMappers.Clear();
        cfg.CreateMap<ScheduledDeduction, ScheduledDeductionDetailVM>();
    });
    

    It will also break behaviour you observe, because you removed member naming conventions.