如何忽略基于运行时条件的属性

本文关键字:条件 属性 运行时 何忽略 | 更新日期: 2023-09-27 18:31:01

我有一对简单的类,我在初始化时设置了一个映射。

public class Order {
  public int ID { get; set; }  
  public string Foo { get; set; }
}
public class OrderDTO {
  public int ID { get; set; }
  public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>();

现在在某个时候我需要将Order映射到OrderDTO.但根据某些情况,我可能需要在映射过程中忽略Foo。我们还假设我无法将条件"存储"在源对象或目标对象中。

我知道如何在初始化时配置忽略的属性,但我不知道如何实现这样的动态运行时行为。

任何帮助将不胜感激。

更新

我这种行为的用例是这样的。我有一个 ASP.NET MVC 网络网格视图,它显示了OrderDTO的列表。用户可以单独编辑单元格值。网格视图将编辑后的数据发送回服务器,就像 OrderDTO 的集合一样,但仅设置了编辑的字段值,其他字段值保留为默认值。它还发送有关为每个主键编辑哪些字段的数据。现在,从这个特殊场景中,我需要将这些"半空"对象映射到 Order s,但当然,跳过那些未为每个对象编辑的属性。

另一种方法是进行手动映射,或者以某种方式使用反射,但我只是在考虑是否可以以某种方式使用AutoMapper。

如何忽略基于运行时条件的属性

我已经深入研究了AutoMapper源代码和示例,发现有一种方法可以在映射时传递运行时参数。

快速示例设置和用法如下所示。

public class Order {
  public int ID { get; set; }  
  public string Foo { get; set; }
}
public class OrderDTO {
  public int ID { get; set; }
  public string Foo { get; set; }
}
...
Mapper.CreateMap<Order, OrderDTO>()
  .ForMember(e => e.Foo, o => o.Condition((ResolutionContext c) => !c.Options.Items.ContainsKey("IWantToSkipFoo")));
...
var target = new Order();
target.ID = 2;
target.Foo = "This should not change";
var source = new OrderDTO();
source.ID = 10;
source.Foo = "This won't be mapped";
Mapper.Map(source, target, opts => { opts.Items["IWantToSkipFoo"] = true; });
Assert.AreEqual(target.ID, 10);
Assert.AreEqual(target.Foo, "This should not change");

事实上,这看起来很"技术性",但我仍然认为有很多用例真的很有帮助。如果此逻辑根据应用程序需求进行通用化,并包装到一些扩展方法中,那么它可能会干净得多。

扩展BlackjacketMack对其他人的评论:

在 MappingProfile 中,向构造函数添加ForAllMaps(...)调用。

using AutoMapper;
using System.Collections.Generic;
using System.Linq;
public class MappingProfile : Profile
{
    public MappingProfile()
    {
        ForAllMaps((typeMap, mappingExpression) =>
        {
            mappingExpression.ForAllMembers(memberOptions =>
            {
                memberOptions.Condition((o1, o2, o3, o4, resolutionContext) =>
                {
                    var name = memberOptions.DestinationMember.Name;
                    if (resolutionContext.Items.TryGetValue(MemberExclusionKey, out object exclusions))
                    {
                        if (((IEnumerable<string>)exclusions).Contains(name))
                        {
                            return false;
                        }
                    }
                    return true;
                });
            });
        });
    }
    public static string MemberExclusionKey { get; } = "exclude";
}

然后,为了便于使用,请添加以下类以为自己创建扩展方法。

public static class IMappingOperationOptionsExtensions
{
    public static void ExcludeMembers(this AutoMapper.IMappingOperationOptions options, params string[] members)
    {
        options.Items[MappingProfile.MemberExclusionKey] = members;
    }
}

最后,将它们全部捆绑在一起:var target = mapper.Map<Order>(source, opts => opts.ExcludeMembers("Foo"));