我有一个涉及.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; }
}
要考虑跟随者和跟随者的映射方式不同,我不得不创建两个相同的类:NetworkUserViewModel
和FollowerViewModel
,以便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逻辑,我将开放几天的时间...
答案 0 :(得分: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;
}
}
并像这样将其添加到 Mapper 配置中:
CreateMap<SourceObjectA, MyResponseObject>()
.ForMember(dest => dest.ObjectA, src => src.MapFrom<MappingResolver>());
CreateMap<SourceObjectB, MyResponseObject>()
.ForMember(dest => dest.ObjectB, src => src.MapFrom<MappingResolver>());