AutoMapper使用相同属性名称进行双向映射

7

考虑这两个对象:

public class UserModel
{
    public string Name {get;set;}
    public IList<RoleModel> Roles {get;set;}
}

public class UserViewModel 
{
    public string Name {get;set;}
    public IList<RoleViewModel> Roles {get;set;} // notice the ViewModel
}

这是最优的映射方式吗?或者说AutoMapper能否自动将Roles映射到Roles

应用程序配置

Mapper.CreateMap<UserModel, UserViewModel>()
    .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Mapper.CreateMap<UserViewModel, UserModel>()
    .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));

实现

_userRepository.Create(Mapper.Map<UserModel>(someUserViewModelWithRolesAttached);
4个回答

16

这是最优的映射方式吗?还是AutoMapper可以自己将Roles映射到Roles?

如果属性名称相同,您不必手动提供映射:

Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();

只需确保内部类型也被映射(RoleViewModelRoleModel)。

然而,这意味着如果您更改源或目标属性名称,AutoMapper映射可能会默默失败并导致难以跟踪的问题(例如,如果您将UserModel.Roles更改为UserModel.RolesCollection而未更改UserViewModels.Roles)。

AutoMapper提供了一个Mapper.AssertConfigurationIsValid()方法,它将检查所有映射是否存在错误并捕获配置不正确的映射。最好编写一个单元测试,在构建时运行,以验证您的映射是否存在此类问题。


你打败了我。:) 所以给你加一分。 - Cubicle.Jockey
Automapper会自动判断集合对象是否不同? - Chase Florell
相同的属性名称会自动映射,因此称为AutoMapper。如果您需要将不同的属性转换为另一个属性,则需要手动将不同名称的属性映射到它们需要去的位置。 - Cubicle.Jockey
1
“只需确保内部类型也被映射(RoleView <--> RoleModel),这样就更清晰易懂了。” - Chase Florell
好的观点。在我的静态配置的末尾放置Mapper.AssertConfigurationIsValid()也是有效的吗? - Chase Florell
@ChaseFlorell:我想你可以把它放在那里,但我认为它更适用于单元测试场景。 - Andrew Whitaker

15

你不需要映射属性。只需确保属性名称匹配,并且它们之间有定义的映射。

Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Mapper.CreateMap<RoleModel, RoleViewModel>();
Mapper.CreateMap<RoleViewModel, RoleModel>();

或者用我刚发现的更酷的方式:

Mapper.CreateMap<UserModel, UserViewModel>().ReverseMap();
Mapper.CreateMap<RoleModel, RoleViewModel>().ReverseMap();

7
谢谢你关于.ReverseMap的提示,我还没有习惯使用它。 - Andrew Whitaker
2
这是一个很棒的功能(.ReverseMap())。 - Chase Florell

0

其他所有答案都更好(我给每个答案点了赞)。

但是我想在这里发布的是一个快速的游乐场,您可以将其复制并粘贴到LinqPad中的C#程序模式中,并在不干扰实际代码的情况下玩耍。

将所有转换移动到TyperConverter类中的另一个很棒的事情是,您现在可以对转换进行单元测试。 :)

在这里,您会注意到模型和视图模型几乎完全相同,除了一个属性。但是通过此过程,正确的属性将转换为目标对象中的正确属性。

将此代码复制到LinqPad中,然后在切换到C#程序模式后,您可以使用播放按钮运行它。

void Main()
{
    AutoMapper.Mapper.CreateMap<UserModel, UserViewModel>().ConvertUsing(new UserModelToUserViewModelConverter());
    AutoMapper.Mapper.AssertConfigurationIsValid();

    var userModel = new UserModel
    {
        DifferentPropertyName = "Batman",
        Name = "RockStar",
        Roles = new[] {new RoleModel(), new RoleModel() }
    };

    var userViewModel = AutoMapper.Mapper.Map<UserViewModel>(userModel);
    Console.WriteLine(userViewModel.ToString());
}

// Define other methods and classes here
public class UserModel
{
    public string Name {get;set;}
    public IEnumerable<RoleModel> Roles { get; set; }
    public string DifferentPropertyName { get; set; }
}

public class UserViewModel 
{
    public string Name {get;set;}
    public IEnumerable<RoleModel> Roles { get; set; } // notice the ViewModel
    public string Thingy { get; set; }

    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine(string.Format("Name: {0}", Name));
        sb.AppendLine(string.Format("Thingy: {0}", Thingy));
        sb.AppendLine(string.Format("Contains #{0} of roles", Roles.Count()));

        return sb.ToString();
    }
}

public class UserModelToUserViewModelConverter : TypeConverter<UserModel, UserViewModel>
{
    protected override UserViewModel ConvertCore(UserModel source)
    {
        if(source == null)
        {
            return null;
        }

        //You can add logic here to deal with nulls, empty strings, empty objects etc
        var userViewModel = new UserViewModel
        {
             Name = source.Name,
             Roles = source.Roles, 
             Thingy = source.DifferentPropertyName
        };
        return userViewModel;
    }
}

public class RoleModel
{
    //no content for ease, plus this has it's own mapper in real life
}

来自 Console.WriteLine(userViewModel.ToString()); 的结果:

Name: RockStar
Thingy: Batman
Contains #2 of roles

0

在 Startup.cs 文件的 Configure() 方法中:

Mapper.Initialize(config => {
                    config.CreateMap<UserModel, UserViewModel>().ReverseMap();
                    // other maps you want to do.
                });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接