如何使用基本属性从 ViewModel 映射到模型

How to map from ViewModel to Model with base properties

我想将 ViewModel 映射到具有基础 class 的模型,但不将基础 class 添加到 ViewModel。

我想保留 classes 的结构。

我试过使用 Include、IncludeBase、IncludeDerivered,但找不到合适的配置。

这是我的代码

public class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        var mapper = new MapperConfiguration(mc => mc.AddProfile(new MappingProfile())).CreateMapper();

        var model = new RegisterViewModel { Name = "TEST" };

        var x = mapper.Map<Register>(model, Guid.NewGuid());
    }
}

public interface ICommand
{
    Guid Id { get; }

    Guid AggregateId { get; }
}

public abstract class Command : ICommand
{
    protected Command() => Id = Guid.NewGuid();

    public Guid Id { get; }

    public Guid AggregateId { get; private set; }
}

public sealed class Register : Command
{
    public string Name { get; private set; }
}

public class RegisterViewModel
{
    public string Name { get; set; }
}

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<RegisterViewModel, Command>()
           .ForMember(x => x.Id, opt => opt.Ignore())
           .AddCommandFields()
           .Include<RegisterViewModel, Register>();

        CreateMap<RegisterViewModel, Register>().IncludeBase<RegisterViewModel, Command>().AddCommandFields();
    }
}

public static class MapperExtensions
{
    private static readonly string AGGREGATE_KEY = "AGGREGATE";

    public static TDestination Map<TDestination>(this IMapper mapper, object source, Guid aggregateId)
    {
        return mapper.Map<TDestination>(source, opt =>
        {
            opt.Items[AGGREGATE_KEY] = aggregateId;
        });
    }

    public static IMappingExpression<TSource, TDestination> AddCommandFields<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
        where TDestination : ICommand => expression.ForMember(x => x.AggregateId, opt => opt.MapFromAggregate());

    private static void MapFromAggregate<TSource, TDestination>(this IMemberConfigurationExpression<TSource, TDestination, Guid> expression) => expression.MapFrom((_, __, ___, context) => context.Items[AGGREGATE_KEY]);
}

尽管调用了 MapFromAggregate,但对于派生属性 Automapper 可以正常工作,但基本属性未映射。

编辑

正如@Lucian Bargaoanu 建议的那样,我想删除扩展方法,但后来我发现问题出在方法 "AddCommandFields".

这段代码可以正常工作

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<RegisterViewModel, Command>()
           .ForMember(x => x.Id, opt => opt.Ignore())
           .ForMember(x => x.AggregateId, opt => opt.MapFromAggregate())
           .Include<RegisterViewModel, Register>();

        CreateMap<RegisterViewModel, Register>()
           .IncludeBase<RegisterViewModel, Command>()
          .ForMember(x => x.AggregateId, opt => opt.MapFromAggregate());
    }
}

public static class MapperExtensions
{
    private static readonly string AGGREGATE_KEY = "AGGREGATE";

    public static TDestination Map<TDestination>(this IMapper mapper, object source, Guid aggregateId)
    {
        return mapper.Map<TDestination>(source, opt =>
        {
            opt.Items[AGGREGATE_KEY] = aggregateId;
        });
    }

    public static void MapFromAggregate<TSource, TDestination>(this IMemberConfigurationExpression<TSource, TDestination, Guid> expression) => expression.MapFrom((_, __, ___, context) => context.Items[AGGREGATE_KEY]);
}

我不知道为什么这个扩展方法会破坏我的代码。

问题是通用约束是针对 ICommand 而 setter 是针对 ICommand.AggregateId 的。您可以将约束更改为 Command 或为 ICommand.AggregateId 添加 setter。