Search code examples
c#asp.netmany-to-manyautomapper

How can I map List<string> in ViewModel to string in another entity using Automapper?


I have 4 entities:

public class Game
    {
        public int Id { get; set; }
        public string GameName { get; set; }
        public int DeveloperStudioId { get; set; }
        public DeveloperStudio DeveloperStudio { get; set; }
        public ICollection<GameGenre> GameGenres { get; set; }
    }
public class DeveloperStudio
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
public class Genre
    {
        public int GenreId { get; set; }
        public string GenreName { get; set; }
        public ICollection<GameGenre> GameGenres { get; set; }
    }
public class GameGenre
    {
        public int GameId { get; set; }
        public Game Game { get; set; }
        public int GenreId { get; set; }
        public Genre Genre { get; set; }
    }

and 1 ViewModel:

public class GameVM
    {
        public string GameName { get; set; }
        public string DeveloperStudioName { get; set; }
        public HashSet<string> Genres { get; set; }
    }

And I can't map List in GameVM to GenreName in Genre entity using Automapper no matter what I do. I want to map each string in HashSet Genres to each GenreName that is inside ICollection GameGenres in Game entity. Something like:

CreateMap<GameVM, Game>()
.ForMember(dest=>dest.GameGenres.Select(s=>s.Genre.GenreName), opt=>opt.MapFrom(g=>g.Genres))

And it needs to work =D


Solution

  • You can do it with some customization for the mapping configuration, create the mapping profile as the following:

    public class GameProfile : Profile
        {
            public GameProfile()
            {
                CreateMap<GameVM, Game>()
                    .ForMember(dest => dest.GameGenres, opt => opt.MapFrom(src =>
                        src.Genres.Select(genreName => new GameGenre { Genre = new Genre { GenreName = genreName } }).ToList()))
                    .ForMember(dest => dest.DeveloperStudio, opt => opt.MapFrom(src =>
                        new DeveloperStudio { Name = src.DeveloperStudioName }));
                //CreateMap<GameVM, Game>().ForMember(dest => dest.GameGenres.Select(s => s.Genre.GenreName), opt => opt.MapFrom(g => g.Genres));           
            }
        }
    

    In this configuration, we create a mapping between GameVM and Game, and specify that the GameGenres property should be mapped using a custom function. The custom function uses LINQ to select a new GameGenre object for each genre name in the Genres property of the source object. Each new GameGenre object is initialized with a new Genre object that has its GenreName property set to the corresponding genre name. Finally, the list of GameGenre objects is assigned to the GameGenres property of the destination object. We also map the DeveloperStudioName property of the source object to a new DeveloperStudio object with its Name property set to the same value. Then you can use this mapper after injection as:

    var gameVM = new GameVM
                {
                    GameName = "My Game",
                    DeveloperStudioName = "My Studio",
                    Genres = new HashSet<string> { "Action", "Adventure", "Others" }
                };
    
                var game = mapper.Map<Game>(gameVM);
                Console.WriteLine($"-------------------------------------");
                foreach (var item in game.GameGenres)
                {
                    Console.WriteLine($"GenerName: {item.Genre.GenreName}");
                }
    

    And the out put will be as:

    -----------------------------------------
    GenerName: Action
    GenerName: Adventure
    GenerName: Others