AutoMapper and Interface Collections

I am new to AutoMapper. Sorry if this is too easy.

This is my example domain:

I have a basket. It contains a list of food items. Food is Banana or Pickle.

I have DTOs that reflect every class in the domain. Purpose: From BasketDto, display it and its contents in the basket.

This is the code that fails. After the last line, I have a Recycle Bin, but it is filled with DTO instead of the usual objects :(

class Program { static void Main(string[] args) { Mapper.CreateMap<BasketDto, Basket>(); Mapper.CreateMap<PickleDto, Pickle>(); Mapper.CreateMap<BananaDto, Banana>(); var dto = new BasketDto { Food = new List<IFood> { new PickleDto { Name = "BigPickle" }, new BananaDto { Name = "SmallBanana" }, } }; var basketFromDto = Mapper.Map<Basket>(dto); } } // Domain classes and interfaces -------------- interface IFood { string Name { get; set; } } class Banana : IFood { public string Name { get; set; } } class Pickle : IFood { public string Name { get; set; } } class Basket { public IList<IFood> Food { get; set; } } // DTOs ------------- class BasketDto { public IList<IFood> Food { get; set; } } class PickleDto : IFood { public string Name { get; set; } } class BananaDto : IFood { public string Name { get; set; } } 

What should I do to make Map also use children as an IList? Mappin interfaces and hierarchies are really complex!

Thank you very much.

+4
source share
2 answers

The problem is that AutoMapper does not know how you want to convert it. All Food items are derived from IFood , so displaying its execution is the simplest (and is correct). You can force a matching mapping by creating a TypeConverter - something like this might work:

 public class FoodConverter : TypeConverter<IFood, IFood> { protected override IFood ConvertCore(IFood source) { if (source is PickleDto) return Mapper.Map<Pickle>(source); if (source is BananaDto) return Mapper.Map<Banana>(source); return null; } } 

This can be configured in your mapping as follows:

 Mapper.CreateMap<IFood, IFood>().ConvertUsing<FoodConverter>(); 

Personally, I would have thought a little that he has DTO foods:

 interface IFoodDto { string Name { get; set; } } 

This will slightly change your intention before AutoMapper .

Finally, remember to call AssertConfigurationIsValid in your mapping.

+4
source

While @Mightymuke's answer is correct, based on the original question, there is something else to consider. From an OOP perspective, interfaces describe behavior, and IFood in the example is not behavior. Using the base product class and the built-in inheritance mapping in Automapper , this is more natural:

 namespace Stackoverflow { public class Food { public virtual string Name { get; set; } } public class Banana : Food { } public class Pickle : Food { } public class Basket { public IList<Food> Food { get; set; } } public class FoodDto { public virtual string Name { get; set; } } public class BananaDto : FoodDto { } public class PickleDto : FoodDto { } public class BasketDto { public IList<FoodDto> Food { get; set; } } [TestFixture] public class InheritanceMappingTests { [Test] public void Should_map_inherited_classes() { //arrange var basketDto = new BasketDto { Food = new List<FoodDto> { new BananaDto {Name = "banana"}, new PickleDto {Name = "pickle"} } }; Mapper.CreateMap<FoodDto, Food>() .Include<BananaDto, Banana>() .Include<PickleDto, Pickle>(); Mapper.CreateMap<BananaDto, Banana>(); Mapper.CreateMap<PickleDto, Pickle>(); Mapper.CreateMap<BasketDto, Basket>(); Mapper.AssertConfigurationIsValid(); //act var basket = Mapper.Map<Basket>(basketDto); //assert Assert.That(basket.Food[0].GetType() == typeof(Banana)); Assert.That(basket.Food[0].Name == "banana"); Assert.That(basket.Food[1].GetType() == typeof(Pickle)); Assert.That(basket.Food[1].Name == "pickle"); } } } 
+5
source

Source: https://habr.com/ru/post/1481528/


All Articles