[英]Mapping Collections in Automapper
我正在使用Automapper克隆对象。 我有一个类,其中包含要以非标准方式处理的集合,我将在下面进行解释(我剥离了一些代码来突出显示特定问题):
public class CommunityModel
{
private readonly IUIManaer _uiMgr;
private readonly IMapper _mapper;
private ValidatedCollection<CommunityUserModel, string> _users;
private int _communityIndex = -1;
public CommunityModel( IUIManager uiMgr, IMapper mapper, IElementManager<CommunityUserModel, string> userMgr )
{
_uiMgr = uiMgr ?? throw new NullReferenceException( nameof(uiMgr) );
_mapper = mapper ?? throw new NullReferenceException( nameof(mapper) );
Users = new ValidatedCollection<CommunityUserModel, string>( userMgr );
}
public int CommunityIndex
{
get => _communityIndex;
set
{
if (value < -1) value = -1;
Set(ref _communityIndex, value);
IsNew = value < 0;
}
}
public ValidatedCollection<CommunityModel, string> Collection { get; set; }
public ValidatedCollection<CommunityUserModel, string> Users
{
get => _users;
set
{
ChangeTracker.RegisterCollection(value);
SetAndValidate( ref _users, value );
}
}
}
ValidatedCollection <>是WPF的ObservableCollection的扩展。 CommunityIndex属性唯一地标识CommunityModel实例。 这使我可以通过EqualityComparison()扩展方法使用Automapper.Collection扩展。
我不希望Automapper初始化Users集合,因为它是在类构造函数中初始化的。 但是我希望将User集合的元素从源Users集合克隆到目标Users集合。
Collection集合包含CommunityModel对象的列表,包括具有Collection属性的实例(即Collection是一组同级CommunityModel实例)。 我希望Automapper初始化集合,并最终将除源对象之外的所有CommunityModel兄弟复制到该集合中,然后将目标对象添加到该集合中(即,最后,集合将是一个集合同胞CommunityModel对象,而源CommunityModel被目标CommunityModel代替了)。
我的地图当前定义如下:
CreateMap<CommunityModel, CommunityModel>()
.ForMember(dest=>dest.Users, opt=>opt.Ignore())
.ForMember(dest=>dest.Collection, opt=>opt.Ignore() )
.EqualityComparison((x,y)=> x.CommunityIndex == y.CommunityIndex);
CreateMap<CommunityUserModel, CommunityUserModel>()
.EqualityComparison((x,y) => x.UserIndex == y.UserIndex);
如果我不忽略()Users集合,则Automapper将初始化该集合,这将覆盖构造函数中设置的Users所需的配置,并在我的应用程序中的其他地方引起问题。 但是,如果忽略Ignore()Users集合,则永远不会将其元素从源克隆到目标。 我想要做的是没有Automapper初始化用户,但仍克隆内容。
如果我不忽略Collection集合,则会遇到无限循环和堆栈溢出的情况,我相信,因为克隆Collection的元素涉及到创建拥有Model属性的CommunityModel实例,该实例应该是对要创建的目标对象的引用,而是导致创建另一个相同的目标对象。 我想做的是让Automapper初始化Collection集合,但是>> not <<克隆源元素,我想稍后在AfterMap()调用中必须这样做。
我意识到这有些不可思议,但是我项目的总体设计导致了这些需求。
在Automapper中执行此操作的最佳方法是什么? 即使我正在克隆对象,我是否也应该考虑创建自定义值解析器,因此源和目标之间的属性名称相同?
我将描述如何解决我的问题,但我不会将其标记为答案,因为我对Automapper不够熟悉,无法知道我认为它在做什么,而实际上是在做什么。
当Automapper映射集合(即使已安装并激活Automapper.Collections库)时,似乎正在发生的事情是,即使源集合和目标集合中元素的类型可以是“不同”,也认为集合是“不同的”如果源和目标集合类型不同,则自动映射。
例如,如果源社区对象具有CommunityUsers的List <>:
public class Community
{
public string Name { get; set; }
public string SiteUrl { get; set; }
public string LoginUrl { get; set; }
public List<CommunityUser> Users { get; set; }
}
public class CommunityUser
{
public string UserID { get; set; }
public string VaultKeyName { get; set; }
}
并且您想要将其映射到目标对象,如下所示:
public class CommunityModel
{
// omitting constructor, which contains logic to
// initialize the Users property based on constructor arguments
public string Name {get;set;}
public string LoginUrl {get;set;}
public string SiteUrl {get;set;}
public ValidatedCollection<CommunityUserModel, string> Users
{ get; set;}
}
public class CommunityUserModel
{
public string UserID {get; set;}
public string VaultKeyName {get;set;}
}
即使所有属性名称都可以由Automapper识别,并且两个Users集合都是IEnumerable,但是两个集合类型不同的事实显然会使Automapper将这些集合视为“不同”。
显然,这意味着Automapper.Collections的添加/删除/复制逻辑不会被使用,即使存在也是如此。 而是,Automapper尝试创建(在本例中)ValidatedCollection的实例,从源对象集合填充它,然后将其分配给目标对象集合。
如果ValidatedCollection <>没有必需的构造方法参数,那很好。 但是,如果这样做,它将失败。 我的情况就是这样。
我的解决方法是在Mapper定义中执行此操作:
CreateMap<Community, CommunityModel>()
.ForMember(dest=>dest.Users, opt=> opt.Ignore())
.AfterMap( ( src, dest, rc ) =>
{
foreach( var srcUser in src.Users )
{
dest.Users.Add(rc.Mapper.Map<CommunityUserModel>(srcUser));
}
} );
这使Automapper不会对目标Users属性(在CommunityModel构造函数中初始化)进行任何操作,并在完成“自动”映射后在源User对象上进行映射。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.