使用Automapper映射自引用关系

Win*_*rpe 7 c# automapper asp.net-core

我有一个涉及.NET Core的自引用关系ApplicationUser

public class Network
{
    public ApplicationUser ApplicationUser { get; set; }
    public string ApplicationUserId { get; set; }
    public ApplicationUser Follower { get; set; }
    public string FollowerId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这样就可以在用户模型中保留“关注者”和“关注者”的列表:

public class ApplicationUser : IdentityUser
{
    //...
    public ICollection<Network> Following { get; set; }
    public ICollection<Network> Followers { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我使用Automapper将关注者和关注列表映射到视图模型。这是视图模型:

public class UserProfileViewModel
{
    //...
    public IEnumerable<FollowerViewModel> Followers { get; set; }
    public IEnumerable<NetworkUserViewModel> Following { get; set; }
}

public class NetworkUserViewModel
{
    public string UserName { get; set; }
    public string ProfileImage { get; set; }
    public bool IsFollowing { get; set; } = true;
    public bool IsOwnProfile { get; set; }
}

public class FollowerViewModel
{
    public string UserName { get; set; }
    public string ProfileImage { get; set; }
    public bool IsFollowing { get; set; } = true;
    public bool IsOwnProfile { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

为了考虑跟随者和跟随者映射的不同方式,我不得不创建两个相同的类:NetworkUserViewModel并且FollowerViewModelAutomapper映射逻辑可以区分如何映射跟随者和如何映射跟随者。这是映射配置文件:

         CreateMap<Network, NetworkUserViewModel>()
         .ForMember(x => x.UserName, y => y.MapFrom(x => x.ApplicationUser.UserName))
         .ForMember(x => x.ProfileImage, y => y.MapFrom(x => x.ApplicationUser.ProfileImage))
         .ReverseMap();

        CreateMap<Network, FollowerViewModel>()
        .ForMember(x => x.UserName, y => y.MapFrom(x => x.Follower.UserName))
        .ForMember(x => x.ProfileImage, y => y.MapFrom(x => x.Follower.ProfileImage))
        .ReverseMap();

        CreateMap<ApplicationUser, UserProfileViewModel>()
          .ForMember(x => x.UserName, y => y.MapFrom(x => x.UserName))
          .ForMember(x => x.Followers, y => y.MapFrom(x => x.Followers))
          .ForMember(x => x.Following, y => y.MapFrom(x => x.Following))
          .ReverseMap();
Run Code Online (Sandbox Code Playgroud)

您可以看到关注者的映射方式类似于,.MapFrom(x => x.Follower.UserName))而关注用户的映射方式则类似于.MapFrom(x => x.ApplicationUser.UserName))

我的问题是:是否可以定义映射关注者和关注用户的不同方式,而不必定义重复的类?我宁愿使用NetworkUserViewModelfor跟随者跟随者;因此FollowerViewModel,如果可能的话,我不需要重复的副本。有办法吗?

更新资料

我已经实现了@Matthijs的建议(请参阅第一个评论),以使用继承来避免不必要的属性重复。我的视图模型现在是这些:

public class UserProfileViewModel
{
    //...
    public IEnumerable<FollowerViewModel> Followers { get; set; }
    public IEnumerable<FollowingViewModel> Following { get; set; }
}

public class NetworkUserViewModel
{
    public string UserName { get; set; }
    public string ProfileImage { get; set; }
    public bool IsFollowing { get; set; }
    public bool IsOwnProfile { get; set; }
}

public class FollowingViewModel : NetworkUserViewModel
{
}

public class FollowerViewModel : NetworkUserViewModel
{
}
Run Code Online (Sandbox Code Playgroud)

对Automapper逻辑进行以下更改:

         CreateMap<Network, FollowingViewModel>()
         .ForMember(x => x.UserName, y => y.MapFrom(x => x.ApplicationUser.UserName))
         .ForMember(x => x.ProfileImage, y => y.MapFrom(x => x.ApplicationUser.ProfileImage))
         .ReverseMap();

        CreateMap<Network, FollowerViewModel>()
        .ForMember(x => x.UserName, y => y.MapFrom(x => x.Follower.UserName))
        .ForMember(x => x.ProfileImage, y => y.MapFrom(x => x.Follower.ProfileImage))
        .ReverseMap();

        CreateMap<ApplicationUser, UserProfileViewModel>()
          .ForMember(x => x.UserName, y => y.MapFrom(x => x.UserName))
          .ForMember(x => x.Followers, y => y.MapFrom(x => x.Followers))
          .ForMember(x => x.Following, y => y.MapFrom(x => x.Following))
          .ReverseMap();
Run Code Online (Sandbox Code Playgroud)

这种重构减少了重复,并使映射逻辑更易于遵循。

我将开放几天,以防万一有一个解决方案完全依赖于Automapper逻辑...

小智 1

您遇到问题的原因是目标对象是相同的,但 Automapper 无法推断它。Automapper 只是使用反射来查找具有相同名称的变量。

对于复杂的映射,下次可以考虑编写自定义映射器。这为您提供了极大的灵活性,并简化了您的映射配置。您可以创建自定义映射解析器以从任何源返回所需的对象,即使您指定了多个源也是如此。您将源映射到您自己的目标成员。我发现这非常好,将来可能会对您有所帮助。它的工作原理如下:

解析器:

public class MappingResolver: IValueResolver<object, object, MyResponseObject>
{
    // Automapper needs parameterless constructor
    public MappingResolver()
    {
    }

    // Resolve dependencies here if needed
    public MappingResolver(IWhateverINeed whateverINeed)
    {
}

    public MyResponseObject Resolve(
        object source, object destination, MyResponseObject> destMember, ResolutionContext context)
    {
        if (source.GetType() == typeof(NetworkUserViewModel) 
        {
          // Specific logic for source object, while destination remains the same response
          var castedObject = source as NetworkUserViewModel;
          return MyResponseObject;
      }
Run Code Online (Sandbox Code Playgroud)

}

并将其添加到 Mapper 配置中:

CreateMap<SourceObjectA, MyResponseObject>()
 .ForMember(dest => dest.ObjectA, src => src.MapFrom<MappingResolver>());

CreateMap<SourceObjectB, MyResponseObject>()
 .ForMember(dest => dest.ObjectB, src => src.MapFrom<MappingResolver>());
Run Code Online (Sandbox Code Playgroud)