namespace AutoMapperVsEfCore
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Threading.Tasks;
using AutoMapper.EquivalencyExpression;
using Microsoft.EntityFrameworkCore;
private static async Task Main(string[] args)
var mapper = CreateMapper();
var container = new Container("Container-Id 000", new List<Item> { new Item("Item-Id 000") { Name = "Item-Name" } });
var containerModel = mapper.Map<ContainerModel>(container);
await using (var ctx = new MyContext())
await ctx.Containers.AddAsync(containerModel);
await ctx.SaveChangesAsync();
await using (var ctx = new MyContext())
var readContainerModel = await ctx.Containers.Include(item => item.Items).FirstOrDefaultAsync();
var readContainer = mapper.Map<Container>(readContainerModel);
readContainer.Items[0].Name += " -- changed";
mapper.Map(readContainer, readContainerModel);
if (readContainer.Items[0].Name != readContainerModel.Items[0].Name)
throw new InvalidOperationException("The names of dto and model doesn't match!");
await ctx.SaveChangesAsync();
private static IMapper CreateMapper()
var config = new MapperConfiguration(
cfg.CreateMap<Container, ContainerModel>(MemberList.None)
.EqualityComparison((src, dst) => src.Id == dst.Id)
.ForMember(dst => dst.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dst => dst.Items, opt => opt.MapFrom(src => src.Items));
cfg.CreateMap<ContainerModel, Container>(MemberList.None)
.EqualityComparison((src, dst) => src.Id == dst.Id)
.ForMember(dst => dst.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dst => dst.Items, opt => opt.MapFrom(src => src.Items));
cfg.CreateMap<IReadOnlyList<Item>, List<ItemModel>>(MemberList.None)
.ConstructUsing((src, ctx) => src.Select(ctx.Mapper.Map<ItemModel>).ToList());
cfg.CreateMap<List<ItemModel>, IReadOnlyList<Item>>(MemberList.None)
.ConstructUsing((src, ctx) => src.Select(ctx.Mapper.Map<Item>).ToList().AsReadOnly());
cfg.CreateMap<Item, ItemModel>(MemberList.None)
.EqualityComparison((src, dst) => src.Id == dst.Id)
.ForMember(dst => dst.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dst => dst.ParentId, opt => opt.Ignore())
.ForMember(dst => dst.Parent, opt => opt.Ignore());
cfg.CreateMap<ItemModel, Item>(MemberList.None)
.EqualityComparison((src, dst) => src.Id == dst.Id)
.ForMember(dst => dst.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.Name))
.ForSourceMember(dst => dst.ParentId, opt => opt.DoNotValidate())
.ForSourceMember(dst => dst.Parent, opt => opt.DoNotValidate());
var result = config.CreateMapper();
result.ConfigurationProvider.AssertConfigurationIsValid();
public Container(string id, IReadOnlyList<Item> items)
public string Id { get; }
public IReadOnlyList<Item> Items { get; }
public string Id { get; }
public string Name { get; set; }
public class ContainerModel
public string Id { get; set; }
public List<ItemModel> Items { get; set; }
public string Id { get; set; }
[ForeignKey(nameof(Parent))]
public string ParentId { get; set; }
public ContainerModel Parent { get; set; }
public string Name { get; set; }
public class MyContext : DbContext
public DbSet<ContainerModel> Containers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
optionsBuilder.UseInMemoryDatabase("testing");
base.OnConfiguring(optionsBuilder);