如何同时获取映射的源和目标成员以在 AutoMapper 中创建自定义映射?
How to get both mapped source and destination member to create a custom mapping in AutoMapper?
例如,假设我需要映射这些 类:
public class Dto
{
public List<string> Items { get; set; } = new List<string> { "orange", "apple" }
}
public class DomainObject
{
public List<string> Items { get; set; }
}
...我想使用 AutoMapper 配置自定义映射,其中 returns DomainObject
在执行某些操作后 Dto
和 DomainObject
必须在场,或者至少我应该能够同时获得来源 Items
和目的地 Items
.
为什么我需要这个?
因为我正在将 DTO 映射到更改跟踪对象,我需要提供一种自定义方法来映射集合,以免在映射操作之前丢失跟踪信息。
目前,我已经使用扩展方法在代码中实现了整个集合映射,但我想将此扩展方法作为 AutoMapper 配置的一部分进行调用,以保持代码尽可能简单并保持 DRY .
可能的部分解决方案
我发现 AutoMapper 具有以下 IMappingExpression<TSource, TDestination>
方法:
IMappingExpression<TSource, TDestination> AfterMap(Action<TSource, TDestination> afterFunction);
虽然这解决了我 90% 的问题,但仍然存在问题。 AfterMap
没有给出 current IMapper
的实例。我不再使用静态 Mapper
,但我在 Castle Windsor 上为 IMapper
配置了一个自定义工厂,因此我可以将它注入到任何地方。
我还需要一个 current IMapper
的实例。我可以解决 IMapper
a la Service Locator 反模式,但我想知道 AutoMapper 是否有另一种方法来完成已经做的事情 AfterMap
也许它可以给当前 IMapper
某种方式...
在映射配置期间使用 AfterMap
不是 100% 令人满意的解决方案
这是我的实际情况:
mapperConfig.CreateMap<CustomerUpdateDto, Customer>()
.ForMember(c => c.Locations, m => m.Ignore())
.ForMember(c => c.Aliases, opts => opts.Ignore())
.ForMember(c => c.Contacts, opts => opts.Ignore())
.ForMember(c => c.Activities, opts => opts.Ignore())
.AfterMap
(
(dto, customer) =>
{
// AHHH! Dependency injection, please ;)
IMapper mapper = Container.Current.Resolve<IMapper>();
dto.Aliases.MapTo<ISet<CustomerAlias>, ISet<CustomerAlias>, CustomerAlias>(customer.Aliases, mapper);
dto.Activities.MapTo<ISet<Activity>, ISet<Activity>, Activity>(customer.Activities, mapper);
dto.Locations.MapTo<ISet<Location>, ISet<Location>, Location>(customer.Locations, mapper);
}
)
.ForAllMembers(options => options.Condition(src => !src.IsSourceValueNull));
MapTo
是一种自定义扩展方法,它概括了一种解决方案,以避免创建整个集合的新实例的默认 AutoMapper 行为,而不是能够将每个集合项从源对象映射到目标对象:
public static class CollectionMappingExtensions
{
public static void MapTo<TSource, TTarget, TItem>(this TSource source, TTarget target, IMapper mapper)
where TSource : ICollection<TItem>
where TTarget : ICollection<TItem>
{
foreach (TItem item in source)
{
TItem targetItem = target.SingleOrDefault(someItem => someItem.Equals(someItem));
if (targetItem == null)
target.Add(item);
else
mapper.Map(item, targetItem);
}
}
}
顺便说一句,如果我能避免使用静态 服务定位器 而不是依赖项注入来获取 IMapper
那就太好了。
例如,假设我需要映射这些 类:
public class Dto
{
public List<string> Items { get; set; } = new List<string> { "orange", "apple" }
}
public class DomainObject
{
public List<string> Items { get; set; }
}
...我想使用 AutoMapper 配置自定义映射,其中 returns DomainObject
在执行某些操作后 Dto
和 DomainObject
必须在场,或者至少我应该能够同时获得来源 Items
和目的地 Items
.
为什么我需要这个?
因为我正在将 DTO 映射到更改跟踪对象,我需要提供一种自定义方法来映射集合,以免在映射操作之前丢失跟踪信息。
目前,我已经使用扩展方法在代码中实现了整个集合映射,但我想将此扩展方法作为 AutoMapper 配置的一部分进行调用,以保持代码尽可能简单并保持 DRY .
可能的部分解决方案
我发现 AutoMapper 具有以下 IMappingExpression<TSource, TDestination>
方法:
IMappingExpression<TSource, TDestination> AfterMap(Action<TSource, TDestination> afterFunction);
虽然这解决了我 90% 的问题,但仍然存在问题。 AfterMap
没有给出 current IMapper
的实例。我不再使用静态 Mapper
,但我在 Castle Windsor 上为 IMapper
配置了一个自定义工厂,因此我可以将它注入到任何地方。
我还需要一个 current IMapper
的实例。我可以解决 IMapper
a la Service Locator 反模式,但我想知道 AutoMapper 是否有另一种方法来完成已经做的事情 AfterMap
也许它可以给当前 IMapper
某种方式...
在映射配置期间使用 AfterMap
不是 100% 令人满意的解决方案
这是我的实际情况:
mapperConfig.CreateMap<CustomerUpdateDto, Customer>()
.ForMember(c => c.Locations, m => m.Ignore())
.ForMember(c => c.Aliases, opts => opts.Ignore())
.ForMember(c => c.Contacts, opts => opts.Ignore())
.ForMember(c => c.Activities, opts => opts.Ignore())
.AfterMap
(
(dto, customer) =>
{
// AHHH! Dependency injection, please ;)
IMapper mapper = Container.Current.Resolve<IMapper>();
dto.Aliases.MapTo<ISet<CustomerAlias>, ISet<CustomerAlias>, CustomerAlias>(customer.Aliases, mapper);
dto.Activities.MapTo<ISet<Activity>, ISet<Activity>, Activity>(customer.Activities, mapper);
dto.Locations.MapTo<ISet<Location>, ISet<Location>, Location>(customer.Locations, mapper);
}
)
.ForAllMembers(options => options.Condition(src => !src.IsSourceValueNull));
MapTo
是一种自定义扩展方法,它概括了一种解决方案,以避免创建整个集合的新实例的默认 AutoMapper 行为,而不是能够将每个集合项从源对象映射到目标对象:
public static class CollectionMappingExtensions
{
public static void MapTo<TSource, TTarget, TItem>(this TSource source, TTarget target, IMapper mapper)
where TSource : ICollection<TItem>
where TTarget : ICollection<TItem>
{
foreach (TItem item in source)
{
TItem targetItem = target.SingleOrDefault(someItem => someItem.Equals(someItem));
if (targetItem == null)
target.Add(item);
else
mapper.Map(item, targetItem);
}
}
}
顺便说一句,如果我能避免使用静态 服务定位器 而不是依赖项注入来获取 IMapper
那就太好了。