用于递归模型的AutoMapper条件映射

时间:2015-07-01 17:42:07

标签: c# automapper

我有一个递归模型类,其定义如下:

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的Child对象,有时我想要包含所有模型。

目前我通过涉及Mapper.Reset()和运行时重新定义地图的令人讨厌的黑客实现了这一点 - 这显然不是很好并且存在多个问题:

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定义返回此层次结构,第二个CreateMap定义(带有Advanced参数)返回此修改后的层次结构(所有Advanced = true模型及其子项从映射中排除):

Root (Advanced = False)
+-Child 2 (Advanced = False)
+-Child 3 (Advanced = False)
  +-Child 3 Sub Child 1 (Advanced = False)

如何使用单个CreateMap定义参数化showAdvanced条件并获得相同的结果?我已经搜索了很多正确的解决方案,尝试使用ResolveUsing,CustomResolvers,但没有用。

3 个答案:

答案 0 :(得分:1)

您可以使用下面给出的自定义转换器,您可以自定义映射设置。

创建转换类

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

答案 1 :(得分:1)

您可以使用上下文选项Items集合在运行时将值传递给map函数:

var showAdvanced = true;
var mappedViewModels = Mapper.Map<ObservableCollection<ItemFilterBlockGroupViewModel>>(
    blockGroups, 
    options => options.Items["includeAdvanced"] = showAdvanced);

并在可用上下文的任何地方使用它们来构造目标对象。     在ResolveUsingConstructUsing方法中,例如:

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


如果使用弱类型字典值来传递flag参数看起来不那么漂亮,我建议将这个逻辑封装在两个独立的方法中,如Martin Fowler article示例。

答案 2 :(得分:0)

您希望使用不属于对象的外部参数来控制逻辑。我知道最好的方法是使用一个映射,并根据你的标志过滤输入对象。像这样的东西

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