如何使用 AutoMapper 将 DTO 展开为 entity/object 属性

How to use AutoMapper to unflatten DTO to entity/object property

这可能是一个非常基本的问题,请原谅我的 AutoMapper 无知。我试图从文档和其他 SO 问题中找出答案,但到目前为止都失败了。也许我正在尝试使用 AutoMapper 来做一些我不应该做的事情。我想在扁平化 DTO 的对象 属性 上设置一个值。

鉴于这些;

public class Fruit
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ColourId { get; set; }
    public Colour Colour { get; set; }
}

public class Colour
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class FruitDto
{
    public string Name { get; set; }
    public string ColourName { get; set; }
}

我可以创建一个扁平化的 DTO 没问题,

var db = _serviceScopeFactory.CreateScope()
    .ServiceProvider.GetService<FruitDb>();
var fruitDtos = db.Fruits.ProjectTo<FruitDto>(_mapper.ConfigurationProvider);
foreach (var dto in fruitDtos)
{
    Console.WriteLine($"db fruit {dto.Name} = {dto.ColourName}");
}

但是当我尝试从 DTO 映射回来时,我并不清楚我需要做什么来设置水果对象颜色 属性 和 Colour.Name 集。

var exampleDto = new FruitDto()
{
    Name = "lime",
    ColourName = "green"
};
var exampleFruit = _mapper.Map<FruitDto, Fruit>(exampleDto);
Console.WriteLine($"example fruit {exampleFruit.Name} = {exampleFruit.Colour?.Name}");

让 AutoMapper 将 exampleFruit.Colour 设置为名称为 属性 的新 Color 实例的正确方法是什么(这个问题的扩展,一旦设置,我应该如何设置colour.Id 属性 如果颜色已经存在于数据库中)?

这是上面片段的完整示例;

using System;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace EntityFrameworkAutomapConsole
{
    public class Fruit
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int ColourId { get; set; }
        public Colour Colour { get; set; }
    }

    public class Colour
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class FruitDto
    {
        public string Name { get; set; }
        public string ColourName { get; set; }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddAutoMapper(typeof(AutoMapperProfile));
                    services.AddDbContext<FruitDb>(options =>
                    {
                        options.UseNpgsql("conn string here");
                    });
                    services.AddHostedService<Worker>();
                });
    }

    public class Worker : BackgroundService
    {
        private readonly IServiceScopeFactory _serviceScopeFactory;
        private readonly IMapper _mapper;

        public Worker(
            IServiceScopeFactory serviceScopeFactory,
            IMapper mapper)
        {
            _serviceScopeFactory = serviceScopeFactory;
            _mapper = mapper;

            var fruitDb = _serviceScopeFactory.CreateScope()
                .ServiceProvider.GetService<FruitDb>();
            fruitDb.Database.EnsureCreated();
        }

        protected override async Task ExecuteAsync(CancellationToken cancel)
        {
            var db = _serviceScopeFactory.CreateScope()
                .ServiceProvider.GetService<FruitDb>();
            var fruitDtos = db.Fruits.ProjectTo<FruitDto>(_mapper.ConfigurationProvider);
            foreach (var dto in fruitDtos)
            {
                Console.WriteLine($"db fruit {dto.Name} = {dto.ColourName}");
            }

            var exampleDto = new FruitDto()
            {
                Name = "lime",
                ColourName = "green"
            };
            var exampleFruit = _mapper.Map<FruitDto, Fruit>(exampleDto);
            Console.WriteLine($"example fruit {exampleFruit.Name} = {exampleFruit.Colour?.Name}");
        }
    }
   
    public class FruitDb : DbContext
    {
        public FruitDb(DbContextOptions options)
            : base(options)
        {
        }

        public DbSet<Fruit> Fruits { get; set; }
        public DbSet<Colour> Colours { get; set; }
        
        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.HasDefaultSchema("public");

            builder.Entity<Fruit>()
                .HasIndex(f => f.Id)
                .IsUnique();
            builder.Entity<Colour>()
                .HasIndex(c => c.Id)
                .IsUnique();

            base.OnModelCreating(builder);
        }
    }

    public class AutoMapperProfile : Profile
    {
        public AutoMapperProfile()
        {
            CreateMap<FruitDto, Fruit>();
            CreateMap<Fruit, FruitDto>();
        }
    }
}

感谢@lucian-bargaoanu,非常简单的评论。我在文档中错过了。所以我只需要在我的配置文件中使用 ReverseMap() 并删除显式 FruitDtoFruit 映射。

public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<FruitDto, Fruit>();
        CreateMap<Fruit, FruitDto>();
    }
}

变成

public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Fruit, FruitDto>().ReverseMap();
    }
}