映射多对多的 C# AutoMapper 问题,错误的外键

C# AutoMapper issue with mapping Many-to-Many, wrong foreign key

我遇到了将模型映射到具有多对多关系的实体的奇怪问题

我有以下实体和模型:

using System;
using System.Diagnostics;
using System.Collections.Generic;

namespace AutoMapper.ReproducedExample
{
    public class StoreEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<StoreProductEntity> Products { get; set; } = new List<StoreProductEntity>();
    }

    public class ProductEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<StoreProductEntity> Stores { get; set; } = new List<StoreProductEntity>();
    }

    public class StoreProductEntity
    {
        public int StoreId { get; set; }
        public StoreEntity Store { get; set; }
        public int ProductId { get; set; }
        public ProductEntity Product { get; set; }
    }

    public class StoreModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<ProductModel> Products { get; set; } = new List<ProductModel>();
    }

    public class ProductModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<StoreModel> Stores { get; set; } = new List<StoreModel>();
    }

    public class CustomProfile : Profile
    {
        public CustomProfile()
        {
            CreateMap<StoreModel, StoreEntity>()
                .ForMember(d => d.Products,
                    opt => opt.MapFrom(s => s.Products))
                .AfterMap((model, entity) =>
                {
                    foreach (var entityProduct in entity.Products)
                    {
                        entityProduct.StoreId = entity.Id;
                        entityProduct.Store = entity;
                    }
                });
            CreateMap<StoreModel, StoreProductEntity>()
                .ForMember(entity => entity.StoreId, opt => opt.MapFrom(model => model.Id))
                .ForMember(entity => entity.Store, opt => opt.MapFrom(model => model))
                .ForMember(entity => entity.ProductId, opt => opt.Ignore())
                .ForMember(entity => entity.Product, opt => opt.Ignore());

            CreateMap<ProductModel, ProductEntity>()
                .ForMember(d => d.Stores,
                    opt => opt.MapFrom(s => s.Stores))
                .AfterMap((model, entity) =>
                {
                    foreach (var entityStore in entity.Stores)
                    {
                        entityStore.ProductId = entity.Id;
                        entityStore.Product = entity;
                    }
                });
            CreateMap<ProductModel, StoreProductEntity>()
                .ForMember(entity => entity.StoreId, opt => opt.Ignore())
                .ForMember(entity => entity.Store, opt => opt.Ignore())
                .ForMember(entity => entity.ProductId, opt => opt.MapFrom(model => model.Id))
                .ForMember(entity => entity.Product, opt => opt.MapFrom(model => model));

        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var configuration = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<CustomProfile>();
            });
#if DEBUG
            configuration.AssertConfigurationIsValid();
#endif
            var mapper = configuration.CreateMapper();
            var store0 = new StoreModel()
            {
                Id = 1,
                Name = "Store0",
            };
            var store1 = new StoreModel()
            {
                Id = 2,
                Name = "Store1",
            };
            var product = new ProductModel()
            {
                Id = 1,
                Name = "Product",
            };
            store1.Products.Add(product);
            product.Stores.Add(store1);
            store0.Products.Add(product);
            product.Stores.Add(store0);
            
            var store0Entity = mapper.Map<StoreEntity>(store0);
            Debug.Assert(store0Entity.Products[0].Product.Stores[0].Store.Id ==
                         store0Entity.Products[0].Product.Stores[0].Store.Products[0].StoreId);
        }
    }
}

映射成功,但由于某些原因,某些深层键未映射到相关 StoreEntity

以下断言失败...

Debug.Assert(store0Entity.Products[0].Product.Stores[0].Store.Id ==
             store0Entity.Products[0].Product.Stores[0].Store.Products[0].StoreId);

似乎出于某种原因深入存储 class AutoMapper 使用了错误的映射列表...但我不确定...

我已经想出如何解决这个问题(感谢对问题 的回答)

为了检测Circular references我可以添加.PreserveReferences()映射方法

这里是正确的CustomProfile:

    public class CustomProfile : Profile
    {
        public CustomProfile()
        {
            CreateMap<StoreModel, StoreEntity>()
                .ForMember(d => d.Products,
                    opt => opt.MapFrom(s => s.Products))
                .AfterMap((model, entity) =>
                {
                    foreach (var entityProduct in entity.Products)
                    {
                        entityProduct.StoreId = entity.Id;
                        entityProduct.Store = entity;
                    }
                })
                .PreserveReferences();
            CreateMap<StoreModel, StoreProductEntity>()
                .ForMember(entity => entity.StoreId, opt => opt.MapFrom(model => model.Id))
                .ForMember(entity => entity.Store, opt => opt.MapFrom(model => model))
                .ForMember(entity => entity.ProductId, opt => opt.Ignore())
                .ForMember(entity => entity.Product, opt => opt.Ignore());

            CreateMap<ProductModel, ProductEntity>()
                .ForMember(d => d.Stores,
                    opt => opt.MapFrom(s => s.Stores))
                .AfterMap((model, entity) =>
                {
                    foreach (var entityStore in entity.Stores)
                    {
                        entityStore.ProductId = entity.Id;
                        entityStore.Product = entity;
                    }
                })
                .PreserveReferences();
            CreateMap<ProductModel, StoreProductEntity>()
                .ForMember(entity => entity.StoreId, opt => opt.Ignore())
                .ForMember(entity => entity.Store, opt => opt.Ignore())
                .ForMember(entity => entity.ProductId, opt => opt.MapFrom(model => model.Id))
                .ForMember(entity => entity.Product, opt => opt.MapFrom(model => model));
        }
    }

但实际上我不知道这是否是一个问题,因为根据文档 AutoMapper 应该自动检测 Circular References AutoMapper 版本 10.0.0 并且看起来这是一个问题