簡體   English   中英

使用 Automapper 映射自引用關系

[英]Using Automapper to map self referencing relationship

我有一個涉及 .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; }
}

這使得在用戶模型中保留“關注者”和“關注者”列表成為可能:

public class ApplicationUser : IdentityUser
{
    //...
    public ICollection<Network> Following { get; set; }
    public ICollection<Network> Followers { get; set; }
}

我使用 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; }
}

為了考慮到FollowerViewModelFollowerViewModel的不同映射方式,我必須創建兩個相同的類: NetworkUserViewModelFollowerViewModel以便 Automapper 映射邏輯可以區分如何映射關注者和如何映射關注者。 這是映射配置文件:

         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();

您可以看到關注者被映射為.MapFrom(x => x.Follower.UserName))而關注用戶被映射為.MapFrom(x => x.ApplicationUser.UserName))

我的問題是:我可以定義映射關注者和關注用戶的不同方式而不必定義重復的類嗎? 我更喜歡將NetworkUserViewModel用於關注者關注者; 所以如果可能的話,我不需要重復的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
{
}

對 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();

這種重構減少了重復並使映射邏輯更易於遵循。

我將這個開放幾天,以防萬一有一個純粹依賴於 Automapper 邏輯的解決方案......

您遇到問題的原因是目標對象相同,但 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;
      }

}

並將其添加到 Mapper 配置中:

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

CreateMap<SourceObjectB, MyResponseObject>()
 .ForMember(dest => dest.ObjectB, src => src.MapFrom<MappingResolver>());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM