How to map a derived class to a base class property with Automapper

automapper c# entity-framework-6

Question

Firstly, if my above question is confusing, I am both new to the use of Automapper and don't know the lingo all that well and rarely ask questions like this so I might be trying to articulate my thoughts into an absurd sounding question.

I have created a few classes that inherit from a base class because I intend to re-use the base classes for more entities than the ones shown below. For simplicity sake, I am going to keep the classes void of the properties.

My issue is with the mapping for the PersonBusiness properties. When I load the data from SQL with EF 6 I see it loads the Person and Business properties of the PersonBusiness object as an Employee object and a Branch object because that is what I am pulling back for the query, but when I attempt to map the Employee Object and the Branch object from the PersonBusiness object to the PersonBusinessDto object it loads them as the Person and Business class types; obviously leaving off all the properties that are included in the derived classes.

It's obvious given the data loaded into the PersonBusiness object that the Person and Business properties are object reference variables that can be loaded with the derived types Employee and Branch but for some reason, Automapper sees the Employee and Branch objects and only maps them to the base type of Person and Business.

This is an example of the data loaded from the database:

This is an example of the data loaded from the database

And this is an example of the data mapped to the PersonBusinessDto:

And this is an example of the data mapped to the PersonBusinessDto

Below are the basic class structure and the mappings I used to map the Entities to the DTOs which work fine for everything except the PersonBusiness mappings.

Entity.cs

public abstract class Entity : IEntity
{

}

EntityDto.cs

public abstract class EntityDto : IEntityDto
{

}

Person.cs

public abstract class Person : Entity
{
    public List<PersonBusiness> PersonBusinesses { get; set; };
}

PersonDto.cs

public abstract class PersonDto : EntityDto
{
    public List<PersonBusinessDto> PersonBusinesses { get; set; };
}

Employee.cs

public class Employee : Person
{

}

EmployeeDto.cs

public class EmployeeDto : PersonDto
{

}

Business.cs

public abstract class Business : Entity
{
    public List<PersonBusiness> PersonBusinesses { get; set; };
}

BusinessDto.cs

public abstract class BusinessDto : EntityDto
{
    public List<PersonBusinessDto> PersonBusinesses { get; set; };
}

Branch.cs

public class Branch : Business
{

}

BranchDto.cs

public class BranchDto : BusinessDto
{

}

PersonBusiness.cs

public class PersonBusiness
{
    public int PersonId { get; set; }
    public int BusinessId { get; set; }
    public Enums.RoleType RoleType { get; set; }

    public Person Person { get; set; }
    public Business Business { get; set; }
}

PersonBusinessDto.cs

public class PersonBusinessDto
{
    public int PersonId { get; set; }
    public int BusinessId { get; set; }
    public Enums.RoleType RoleType { get; set; }

    public PersonDto Person { get; set; }
    public BusinessDto Business { get; set; }
}

AutoMapperConfig.cs

public class ProfileMapping
        : Profile
    {
        private readonly IMapperConfiguration _mappingConfiguration;

        public ProfileMapping(IMapperConfiguration config)
        {
            _mappingConfiguration = config;
        }

        protected override void Configure()
        {
            _mappingConfiguration.AllowNullDestinationValues = true;

            _mappingConfiguration
                .CreateMap<Person, PersonDto>()
                .ForMember(dto => dto.PersonBusinesses, opt => opt.MapFrom(src => src.PresonBusinesses));

            _mappingConfiguration
                .CreateMap<Employee, EmployeeDto>()

            _mappingConfiguration
                .CreateMap<Business, BusinessDto>()
                .ForMember(dto => dto.PersonBusinesses, opt => opt.MapFrom(src => src.PresonBusinesses));

            _mappingConfiguration
                .CreateMap<Branch, BranchDto>();

            _mappingConfiguration
                .CreateMap<PersonBusiness, PersonBusinessDto>()
                .ForMember(dto => dto.Business, opt => opt.MapFrom(src => src.Business))
                .ForMember(dto => dto.Person, opt => opt.MapFrom(src => src.Person));
        }
    }
1
0
1/30/2017 9:38:19 PM

Popular Answer

What's happening here is that Employee and Branch are being upcasted to Person and Business, respectively since those are the actual types in the PersonBusiness class. Then, when AutoMapper does the mapping, it sees Person and Business types and therefore maps them to PersonDto and BusinessDto as the configuration dictates.

The objects are still instances of Employee and Branch, even after they've been upcasted; you could cast back to Employee and Branch. However, AutoMapper is creating actual instances of PersonDto and BusinessDto, so you can't cast those to EmployeDto and BranchDto.

Short of having a class with the actual correct property types, you'll have to cast them back to the right types during the mapping, which is going to be a real pain. Principally, you're going to run into the issue that MapFrom is not going to be able to return varying derived types, because the lambda can only return one type.

AutoMapper provides some guidance for how to make this sort of thing work. However, you'll notice that it's dealing with the instances directly, and not properties on those instances. I'm not sure how to actually, do what you need to do, other than to simply ignore mapping those properties when mapping from PersonBusiness to PersonBusinessDto, and then explicitly map over the properties using the method the AutoMapper docs detail, i.e.

personBusinessDto.Person = Mapper.Map(personBusiness.Person, personBusiness.Person.GetType(), typeof(PersonDto));

Like I said: real pain.

0
11/18/2017 1:03:29 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow