简体   繁体   English

递归模型的 AutoMapper 条件映射

[英]AutoMapper conditional map for recursive model

I've got a recursive model class with the following definition:我有一个具有以下定义的递归模型类:

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

It has a property called ChildGroups which is a list of itself - this is used to build up a hierarchical model.它有一个名为 ChildGroups 的属性,它是一个自身的列表——用于构建层次模型。 What I'm trying to do is map this model to view models, but conditionally.我想要做的是将此模型映射到查看模型,但有条件。 Sometimes (depending on a UI setting) I want to include only Child objects with Advanced = false, and sometimes I want to include all models.有时(取决于 UI 设置)我只想包含 Advanced = false 的 Child 对象,有时我想包含所有模型。

Currently I'm achieving this with a nasty hack that involves Mapper.Reset() and runtime re-definition of the maps - this is obviously not good and presents multiple problems:目前,我正在通过一个涉及 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);

Given an example input hierarchy of models:给定模型的示例输入层次结构:

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)

The first CreateMap definition returns this hierarchy untouched, and the second CreateMap definition (with the Advanced parameter) returns this modified hierarchy (all Advanced = true models and their children are excluded from mapping):第一个 CreateMap 定义返回此层次结构,第二个 CreateMap 定义(带有 Advanced 参数)返回此修改后的层次结构(所有 Advanced = true 模型及其子项都从映射中排除):

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

How can I parameterise the showAdvanced condition and achieve the same result with a single CreateMap definition?如何参数化 showAdvanced 条件并使用单个 CreateMap 定义实现相同的结果? I've searched a lot for the right solution, tried ResolveUsing, CustomResolvers, to no avail.我已经搜索了很多正确的解决方案,尝试了 ResolveUsing、CustomResolvers,但无济于事。

you can use custom converters something like given below , you can customize your mapping setup.您可以使用如下所示的自定义转换器,您可以自定义映射设置。

Create a convert 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;
    }
}

Then add mapping like this.然后像这样添加映射。

Mapper.CreateMap<PD.IAccount, OD.IAccount>()
  .ConvertUsing(new AccountConverter());

You can use context option Items collection to pass values into map function at runtime:您可以使用上下文选项Items集合在运行时将值传递给 map 函数:

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

And use them anywhere where context is available to construct destination object.并在上下文可用于构造目标对象的任何地方使用它们。 Within ResolveUsing or ConstructUsing methods for example:例如在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);               
         }));


If usage of weakly typed dictionary values to pass flag argument look not so pretty to you I suggest to encapsulate this logic within two separated methods, as in Martin Fowler article example.如果使用弱类型字典值传递标志参数对您来说看起来不太好,我建议将此逻辑封装在两个单独的方法中,如Martin Fowler 文章示例中所示。

Well you want to control the logic using a outside parameter that is not part of the object.好吧,您想使用不属于对象的外部参数来控制逻辑。 I bielieve the best way would be to use one mapping, and filter the input object based on your flag.我认为最好的方法是使用一个映射,并根据您的标志过滤输入对象。 Something like this像这样的东西

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM