Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instance creation of derived class from an abstract class #52

Closed
jigneshkvp opened this issue Mar 3, 2016 · 7 comments
Closed

Instance creation of derived class from an abstract class #52

jigneshkvp opened this issue Mar 3, 2016 · 7 comments

Comments

@jigneshkvp
Copy link

Hello @eswann , @chaowlert,

I am facing a particular issue where I have a derived class inheriting from a base abstract class and Mapster give me an error "Instances of abstract classes cannot be created". Consider the example below:

I have a TPT database model, where my Car inherits from Vehicle, my EF model reflects it.

My Dto objects are:


public class ProductDto
{
  public List<VehicleDto> Vehicles { get; set; }
}

[KnownType(typeof(CarDto)]
public abstract class VehicleDto
{
  public int Id { get; set; }
  public string Name { get; set; }
}

public class CarDto : VehicleDto
{
  public string Make { get; set; }
  public string ChassiNumber { get; set; }
}

My Mapping is:

TypeAdapterConfig<Product, ProductDto>.NewConfig(); //Product is a table in db
TypeAdapterConfig<Vehicle, VehicleDto>.NewConfig(); // Vehicle is a table in the db
TypeAdapterConfig<Car, CarDto> // Car is a table in the db
      .NewConfig()
      .Inherits<Vehicle, VehicleDto>;

The actual call

 var product = context.GetProductById(1); // returns the list of Vehicle - containing type Car 
 var productDto = product.Adapt<ProductDto>(); // Exception is throw here. Where I expect my list  of VehicleDto will be mapped (as list of type CarDto)

What wrong am I doing? Is there any mapping registration issue? or Mapster is unable to handle abstract class? Please let me know.

Thanks!

@BuzzDee3000
Copy link

Hi jigneshkvp,
have you tried to define a custom constructor configuration?

//Example using a non-default constructor TypeAdapterConfig<TSource, TDestination>.NewConfig() .ConstructUsing(src => new TDestination(src.Id, src.Name));

@eswann
Copy link
Collaborator

eswann commented Mar 3, 2016

THanks for the heads-up, the abstract type should be ok. I'll add a test case to test this in a bit.

@eswann
Copy link
Collaborator

eswann commented Mar 3, 2016

Ahh...I see the issue now. Because your ProductDto has a list of VehicleDto, it's trying to map to the VehicleDto type (meaning it will try to new up this type). It can't assume you want to map to CarDto just because that is one mapping that is defined. You have to know the destination type you are mapping to. Problem is that if you have several vehicle types, you'll need some kind of logic in the mapping to differentiate the type to map to.

@chaowlert
Copy link
Collaborator

This is interesting issue. I will check how EF can return information from abstract class. For now, could you remove from abstract modifier from VehicleDto class?

@jigneshkvp
Copy link
Author

Hi,

I managed to do something like this as suggested by BuzzDee3000 (Thanks!):

TypeAdapterConfig<Vehicle, VehicleDto>.NewConfig()
.ConstructUsing(src => MapVehicle(src));

public VehicleDto MapVehicle(Vehicle vehicle)
{
  var vehicleType = vehicle.GetType().BaseType;
  if(vehicleType == null) return null;

  if(vehicleType.FullName == typeOf(Car).Name)
      return new CarDto();

 if(vehicleType.FullName == typeOf(Truck).Name)
      return new TruckDto();  

 return null;
}

In AutoMapper 4.2.1, inheritance is handled as follows and it automatically takes care of instantiating concrete classes from abstract classes:

var config = new MapperConfiguration(cfg =>  
                           {
                                  cfg.CreateMap<Vehicle, VehicleDto>()
                                       .Include<Car, CarDto>()
                                       .Include<Truck, TruckDto>();
                           });

It would have been really great if we could have something similar in Mapster.

Also, what I do use in AutoMapper is the "Profile" class, which provides named configuration for maps.

using AutoMapper;
public class VehicleProfile : Profile
{
   protected override void Configure()
    {
         var config = new MapperConfiguration(cfg =>  
                           {
                                  cfg.CreateMap<Vehicle, VehicleDto>()
                                       .Include<Car, CarDto>()
                                       .Include<Truck, TruckDto>();
                           });
    }
}

So I can register/add all profiles during Application_Start and then my Mapping is done once for the session. I then resolve the mapping using the "MappingEngine" class which I pass as a dependency wherever required.

Could you suggest me something similar in Mapster?

Thanks for your quick response! 👍

@eswann
Copy link
Collaborator

eswann commented Mar 7, 2016

Hi @jigneshkvp , it make sense to see if we have a mapping that qualifies as a "default/defined" mapping for that destination type already registered. I'll leave this issue open and take a look.

@chaowlert
Copy link
Collaborator

Hi, I added Include command in Mapster 3.0 (https://github.com/chaowlert/Mapster/wiki/Config-inheritance), please try. Thx u.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants