Cannot get child class after mapping

Viewed 64

I have an Entity and Dto like this:

public class Animal
{
    public int Legs { get; set; }
}
public class Tiger : Animal
{
    public string Description1 { get; set; }
}
public class AnimalDto
{
    public int Legs { get; set; }
}
public class TigerDto : AnimalDto
{
    public string Description1 { get; set; }
}

Here is the code for mapping:

     public void MyTest2()
    {
        //add child to parent
        var tiger = new Tiger() { Description1 = "Tiger", Legs = 4 };
        var animals = new List<Animal>();
        animals.Add(tiger);
        //mapping
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Animal, AnimalDto>();
            cfg.CreateMap<Tiger, AnimalDto>();
            cfg.CreateMap<Tiger, TigerDto>();
        });
        var mapper = config.CreateMapper();
        //transfer to dto
        var animalsDto = mapper.Map<List<AnimalDto>>(animals);
        var tigerDto = mapper.Map<List<TigerDto>>(animals);
        var tigerDto2 = mapper.Map<List<TigerDto>>(animalsDto);
    }

The purpose is:

  1. Create list Animal with a child (animals.Add(tiger))
  2. map information from Entity (Animal) to Dto (AnimalDto)
  3. Send out AnimalDto (via API)
  4. Get AnimalDto and retrieve Child (here is TigerDto)

With mapping above, I can get correct data for tigerDto.

Q1. But how I can get data of tigerDto2?

Q2. How to send out Dto with child data?

Any advise will be appriciated.

1 Answers

An error occurs while execution:

InvalidCastException: Unable to cast object of type 'AnimalDto' to type 'TigerDto'.

It is not eligible to map base class to derived class as base class does not know what values should be set for derived type. Let me clarify with an example:

public class Human
{ }


public class Employee : Human
{
    public DateTime DateHired { get; set; }
}

Now we want to assign instance of Human to Employee variable. But we do not know value of DateHired. So it is not eligible:

Human human = new Human();
Employee employee = human; // compile time error: Cannot 
    // implicitly convert type 'Human' to 'Employee'

This is a reason why it is not eligible to case base class to derived class.

So what you can do is to cast one instance of Animal to Tiger like that (Tiger)s:

List<Tiger> tigers = animals.Select(s => (Tiger)s).ToList(); // you are explicitly 
    // saying to compiler that you know that `s` can be casted to 
    // more derived class such as `Tiger`

So the whole code could look like this:

[Fact]
public void Test1()
{
    // arrange
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Animal, AnimalDto>();
        cfg.CreateMap<Tiger, TigerDto>();
    });
    var mapper = config.CreateMapper();

    Tiger tiger = new Tiger() { Description1 = "Tiger", Legs = 4 };
    List<Animal> animals = new List<Animal>();

    // act
    animals.Add(tiger);
    List<Tiger> tigers = animals
        .Select(s => (Tiger)s).ToList(); // In my view, this code is unnecessary
    var tigerDto = mapper.Map<List<TigerDto>>(tigers); // In my view, this code is unnecessary

    // assert
    Assert.IsType<List<Animal>>(animals);
}

Read more about List covariance limitation

Related