如何在不指定每个成员字段的情况下从元组映射?

How to map from a tuple without specifying each member field?

给定以下示例 类

public class User
{
    public string Username { get; set; }
    public string FieldFromUser { get; set; }
}

public class Todo
{
    public string Title { get; set; } // !! map this one to UsernameWithTodoTitle.TodoTitle !!
    public string FieldFromTodo { get; set; }
}

public class UsernameWithTodoTitle
{
    public string Username { get; set; }
    public string TodoTitle { get; set; } // !! this field represents Todo.Title !!
    public string FieldFromUser { get; set; }
    public string FieldFromTodo { get; set; }
}

我想将用户和待办事项映射到 UsernameWithTodoTitle。为了解决这个问题,我使用了 packages

AutoMapper v10.1.1

AutoMapper.Extensions.Microsoft.DependencyInjection v8.1.1

当处理多个来源时,这个解决方案对我来说很好用:

public class UsernameWithTodoTitleMappingProfile : Profile
{
    public UsernameWithTodoTitleMappingProfile()
    {
        CreateMap<(User, Todo), UsernameWithTodoTitle>()
            .ForMember(
                destination => destination.Username,
                memberOptions => memberOptions.MapFrom(source => source.Item1.Username))
            .ForMember(
                destination => destination.TodoTitle,
                memberOptions => memberOptions.MapFrom(source => source.Item2.Title))
            .ForMember(
                destination => destination.FieldFromUser,
                memberOptions => memberOptions.MapFrom(source => source.Item1.FieldFromUser))
            .ForMember(
                destination => destination.FieldFromTodo,
                memberOptions => memberOptions.MapFrom(source => source.Item2.FieldFromTodo));
    }
}

所以我可以像这样映射用户和待办事项

var usernameWithTodoTitle = _mapper.Map<UsernameWithTodoTitle>((user, todo));

因为我使用的是元组,所以我必须指定每个字段,因为显然 Automapper 在处理元组时不知道在哪里搜索这些字段。唯一不同的字段名称是 Todo.TitleUsernameWithTodoTitle.TodoTitle。我想知道我是否可以简化映射配置文件,也许我什至不必使用元组?以下配置文件只是我想要实现的伪实现

public class UsernameWithTodoTitleMappingProfile : Profile
{
    public UsernameWithTodoTitleMappingProfile()
    {
        CreateMap<(User, Todo), UsernameWithTodoTitle>()
            // map all fields by name as expected and ...
            .ForMember(
                destination => destination.TodoTitle,
                memberOptions => memberOptions.MapFrom(source => source.Item2.Title));
    }
}

正如您已经提到的,您不需要使用 Tuple - 如果确实需要,请参阅下面的更新
您可以映射来自多个源的单个目标对象。

设置从源 UserTodoUsernameWithTodoTitle 目标的常规映射。

CreateMap<User, UsernameWithTodoTitle>()

CreateMap<Todo, UsernameWithTodoTitle>()
    .ForMember(
        destination => destination.TodoTitle,
        memberOptions => memberOptions.MapFrom(source => source.Title)
    );

要实例化一个 UsernameWithTodoTitle 对象,应用两个映射之一;来自 User.

var usernameWithTodoTitle = _mapper.Map<UsernameWithTodoTitle>(user);

其次,您通过应用下一个映射来更新新创建的 UsernameWithTodoTitle;来自 Todo.

_mapper.Map(todo, usernameWithTodoTitle);

更新

如果你真的want/need那个Tuple映射,这样的映射可以如下设置,通过Tuple部分的IncludeMembers规则将触发相应的映射。
这使得仍然需要上面的单独映射规则。

CreateMap<(User, Todo), UsernameWithTodoTitle>()
    .IncludeMembers(o => o.Item1, o => o.Item2);

您可能更喜欢命名为 Tuple 的部分。

CreateMap<(User User, Todo Todo), UsernameWithTodoTitle>()
    .IncludeMembers(o => o.User, o => o.Todo);

完整示例:

CreateMap<User, UsernameWithTodoTitle>();

CreateMap<Todo, UsernameWithTodoTitle>()
    .ForMember(
        destination => destination.TodoTitle,
        memberOptions => memberOptions.MapFrom(source => source.Title)
    );          

CreateMap<(User User, Todo Todo), UsernameWithTodoTitle>()
    .IncludeMembers(o => o.User, o => o.Todo);

var user = new User { Username = "alfki" };
var todo = new Todo { Title = "work" };
var usernameWithTodoTitle = _mapper.Map<UsernameWithTodoTitle>((user, todo));

Console.WriteLine(usernameWithTodoTitle.Username); // alfki
Console.WriteLine(usernameWithTodoTitle.TodoTitle); // work