如何根据运行时条件忽略 属性?
How to ignore a property based on a runtime condition?
我有一对简单的 类,因为我在初始化时设置了一个映射。
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
。我们还假设我不能 "store" 源对象或目标对象中的条件。
我知道如何在初始化时配置被忽略的属性,但我不知道如何实现这种动态运行时行为。
如有任何帮助,我们将不胜感激。
更新
我对这种行为的用例是这样的。我有一个 ASP.NET MVC 网络网格视图,它显示 OrderDTO
的列表。用户可以单独编辑单元格值。网格视图将编辑后的数据像 OrderDTO
的集合一样发送回服务器,但仅设置了编辑后的字段值,其他保留为默认值。它还发送有关为每个主键编辑哪些字段的数据。现在从这个特殊场景我需要将这些 "half-empty" 对象映射到 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");
事实上,这看起来很不错 "technical",但我仍然认为有很多用例,这确实很有帮助。如果这个逻辑根据应用程序的需要进行概括,并包装到一些扩展方法中,那么它可能会更清晰。
扩展 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";
}
然后,为了方便使用,添加下面的class,自己创建一个扩展方法
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"));
我有一对简单的 类,因为我在初始化时设置了一个映射。
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
。我们还假设我不能 "store" 源对象或目标对象中的条件。
我知道如何在初始化时配置被忽略的属性,但我不知道如何实现这种动态运行时行为。
如有任何帮助,我们将不胜感激。
更新
我对这种行为的用例是这样的。我有一个 ASP.NET MVC 网络网格视图,它显示 OrderDTO
的列表。用户可以单独编辑单元格值。网格视图将编辑后的数据像 OrderDTO
的集合一样发送回服务器,但仅设置了编辑后的字段值,其他保留为默认值。它还发送有关为每个主键编辑哪些字段的数据。现在从这个特殊场景我需要将这些 "half-empty" 对象映射到 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");
事实上,这看起来很不错 "technical",但我仍然认为有很多用例,这确实很有帮助。如果这个逻辑根据应用程序的需要进行概括,并包装到一些扩展方法中,那么它可能会更清晰。
扩展 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";
}
然后,为了方便使用,添加下面的class,自己创建一个扩展方法
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"));