递归模型的 AutoMapper 条件映射
AutoMapper conditional map for recursive model
我有一个递归模型class,其定义如下:
public class ItemFilterBlockGroup
{
public ItemFilterBlockGroup(string groupName, ItemFilterBlockGroup parent, bool advanced = false)
{
GroupName = groupName;
ParentGroup = parent;
Advanced = advanced;
ChildGroups = new List<ItemFilterBlockGroup>();
}
public string GroupName { get; private set; }
public bool Advanced { get; private set; }
public ItemFilterBlockGroup ParentGroup { get; private set; }
public List<ItemFilterBlockGroup> ChildGroups { get; private set; }
}
它有一个名为 ChildGroups 的 属性,它本身就是一个列表 - 这用于构建分层模型。我想做的是将此模型映射到视图模型,但有条件。有时(取决于 UI 设置)我只想包含 Advanced = false 的子对象,有时我想包含所有模型。
目前,我正在通过涉及 Mapper.Reset() 和运行时重新定义地图的讨厌的 hack 来实现这一目标 - 这显然不好,并且存在多个问题:
Mapper.Reset();
if (showAdvanced)
{
Mapper.CreateMap<ItemFilterBlockGroup, ItemFilterBlockGroupViewModel>();
}
else
{
Mapper.CreateMap<ItemFilterBlockGroup, ItemFilterBlockGroupViewModel>()
.ForMember(dest => dest.ChildGroups,
opts => opts.MapFrom(from => from.ChildGroups.Where(c => c.Advanced == false)));
}
var mappedViewModels = Mapper.Map<ObservableCollection<ItemFilterBlockGroupViewModel>>(blockGroups);
给定模型输入层次结构示例:
Root (Advanced = False)
+-Child 1 (Advanced = True)
+-Child 2 (Advanced = False)
+-Child 3 (Advanced = False)
+-Child 3 Sub Child 1 (Advanced = False)
+-Child 3 Sub Child 2 (Advanced = True)
第一个 CreateMap 定义 returns 此层次结构未受影响,第二个 CreateMap 定义(使用 Advanced 参数)returns 此修改后的层次结构(所有 Advanced = true 模型及其子项都被排除在映射之外):
Root (Advanced = False)
+-Child 2 (Advanced = False)
+-Child 3 (Advanced = False)
+-Child 3 Sub Child 1 (Advanced = False)
如何参数化 showAdvanced 条件并使用单个 CreateMap 定义获得相同的结果?我搜索了很多正确的解决方案,尝试了 ResolveUsing、CustomResolvers,但无济于事。
您可以使用如下所示的自定义转换器,您可以自定义您的映射设置。
创建转换class
internal class AccountConverter : TypeConverter<PD.IAccount, OD.IAccount>
{
protected override OD.IAccount ConvertCore(PD.IAccount source)
{
var result = new Account()
{
CustomerNumber = source.CustomerNumber,
EAccount = source.EAccount,
EmailAddress = source.EmailAddress
};
return result;
}
}
然后像这样添加映射。
Mapper.CreateMap<PD.IAccount, OD.IAccount>()
.ConvertUsing(new AccountConverter());
好吧,您想使用不属于对象的外部参数来控制逻辑。我相信最好的方法是使用一个映射,并根据您的标志过滤输入对象。像这样
var blockGroupsTemp;
if (showAdvanced)
blockGroupsTemp = blockGroups;
else
{
blockGroupsTemp = blockGroups.Where(x => x.Advanced == false).ToList();
blockGroupsTemp.ForEach(s => s.ChildGroups.RemoveAll(y => y.Advanced == true));
}
var mappedViewModels = Mapper.Map<ObservableCollection<ItemFilterBlockGroupViewModel>>(blockGroupsTemp);
您可以使用上下文选项 Items
集合在运行时将值传递给映射函数:
var showAdvanced = true;
var mappedViewModels = Mapper.Map<ObservableCollection<ItemFilterBlockGroupViewModel>>(
blockGroups,
options => options.Items["includeAdvanced"] = showAdvanced);
并在上下文可用的任何地方使用它们来构造目标对象。
在 ResolveUsing
或 ConstructUsing
方法中,例如:
Mapper.CreateMap<ItemFilterBlockGroup, ItemFilterBlockGroupViewModel>()
.ForMember(destination => destination.ChildGroups, options => options.ResolveUsing(
(resolution) =>
{
var includeAdvanced = (bool)resolution.Context.Options.Items["includeAdvanced"];
var source = (ItemFilterBlockGroup)resolution.Context.SourceValue;
if(includeAdvanced)
return source.ChildGroups;
else
return source.ChildGroups.Where(c => c.Advanced == false);
}));
如果使用弱类型字典值来传递标志参数对您来说看起来不太漂亮,我建议将此逻辑封装在两个单独的方法中,如 Martin Fowler article 示例。
我有一个递归模型class,其定义如下:
public class ItemFilterBlockGroup
{
public ItemFilterBlockGroup(string groupName, ItemFilterBlockGroup parent, bool advanced = false)
{
GroupName = groupName;
ParentGroup = parent;
Advanced = advanced;
ChildGroups = new List<ItemFilterBlockGroup>();
}
public string GroupName { get; private set; }
public bool Advanced { get; private set; }
public ItemFilterBlockGroup ParentGroup { get; private set; }
public List<ItemFilterBlockGroup> ChildGroups { get; private set; }
}
它有一个名为 ChildGroups 的 属性,它本身就是一个列表 - 这用于构建分层模型。我想做的是将此模型映射到视图模型,但有条件。有时(取决于 UI 设置)我只想包含 Advanced = false 的子对象,有时我想包含所有模型。
目前,我正在通过涉及 Mapper.Reset() 和运行时重新定义地图的讨厌的 hack 来实现这一目标 - 这显然不好,并且存在多个问题:
Mapper.Reset();
if (showAdvanced)
{
Mapper.CreateMap<ItemFilterBlockGroup, ItemFilterBlockGroupViewModel>();
}
else
{
Mapper.CreateMap<ItemFilterBlockGroup, ItemFilterBlockGroupViewModel>()
.ForMember(dest => dest.ChildGroups,
opts => opts.MapFrom(from => from.ChildGroups.Where(c => c.Advanced == false)));
}
var mappedViewModels = Mapper.Map<ObservableCollection<ItemFilterBlockGroupViewModel>>(blockGroups);
给定模型输入层次结构示例:
Root (Advanced = False)
+-Child 1 (Advanced = True)
+-Child 2 (Advanced = False)
+-Child 3 (Advanced = False)
+-Child 3 Sub Child 1 (Advanced = False)
+-Child 3 Sub Child 2 (Advanced = True)
第一个 CreateMap 定义 returns 此层次结构未受影响,第二个 CreateMap 定义(使用 Advanced 参数)returns 此修改后的层次结构(所有 Advanced = true 模型及其子项都被排除在映射之外):
Root (Advanced = False)
+-Child 2 (Advanced = False)
+-Child 3 (Advanced = False)
+-Child 3 Sub Child 1 (Advanced = False)
如何参数化 showAdvanced 条件并使用单个 CreateMap 定义获得相同的结果?我搜索了很多正确的解决方案,尝试了 ResolveUsing、CustomResolvers,但无济于事。
您可以使用如下所示的自定义转换器,您可以自定义您的映射设置。
创建转换class
internal class AccountConverter : TypeConverter<PD.IAccount, OD.IAccount>
{
protected override OD.IAccount ConvertCore(PD.IAccount source)
{
var result = new Account()
{
CustomerNumber = source.CustomerNumber,
EAccount = source.EAccount,
EmailAddress = source.EmailAddress
};
return result;
}
}
然后像这样添加映射。
Mapper.CreateMap<PD.IAccount, OD.IAccount>()
.ConvertUsing(new AccountConverter());
好吧,您想使用不属于对象的外部参数来控制逻辑。我相信最好的方法是使用一个映射,并根据您的标志过滤输入对象。像这样
var blockGroupsTemp;
if (showAdvanced)
blockGroupsTemp = blockGroups;
else
{
blockGroupsTemp = blockGroups.Where(x => x.Advanced == false).ToList();
blockGroupsTemp.ForEach(s => s.ChildGroups.RemoveAll(y => y.Advanced == true));
}
var mappedViewModels = Mapper.Map<ObservableCollection<ItemFilterBlockGroupViewModel>>(blockGroupsTemp);
您可以使用上下文选项 Items
集合在运行时将值传递给映射函数:
var showAdvanced = true;
var mappedViewModels = Mapper.Map<ObservableCollection<ItemFilterBlockGroupViewModel>>(
blockGroups,
options => options.Items["includeAdvanced"] = showAdvanced);
并在上下文可用的任何地方使用它们来构造目标对象。
在 ResolveUsing
或 ConstructUsing
方法中,例如:
Mapper.CreateMap<ItemFilterBlockGroup, ItemFilterBlockGroupViewModel>()
.ForMember(destination => destination.ChildGroups, options => options.ResolveUsing(
(resolution) =>
{
var includeAdvanced = (bool)resolution.Context.Options.Items["includeAdvanced"];
var source = (ItemFilterBlockGroup)resolution.Context.SourceValue;
if(includeAdvanced)
return source.ChildGroups;
else
return source.ChildGroups.Where(c => c.Advanced == false);
}));
如果使用弱类型字典值来传递标志参数对您来说看起来不太漂亮,我建议将此逻辑封装在两个单独的方法中,如 Martin Fowler article 示例。