ForAllOtherMembers 扩展方法已从 Automapper 11 中删除 我用它来忽略除前面提到的属性之外的属性的常规映射,如下所示
ForAllOtherMembers(opt=>opt.ignore())
如何在 Automapper 11 中执行此操作?
我永远不会改变我的代码中的哪怕一行,因为 AutoMapper 的作者认为无论出于什么“原因”,这都不是一件“正确”的事情。
快速而肮脏的解决方案,添加单元测试是有意义的:
using AutoMapper.Internal;
using AutoMapper.Configuration;
public static class AutoMapperExtensions
{
private static readonly PropertyInfo TypeMapActionsProperty = typeof(TypeMapConfiguration).GetProperty("TypeMapActions", BindingFlags.NonPublic | BindingFlags.Instance);
// not needed in AutoMapper 12.0.1
private static readonly PropertyInfo DestinationTypeDetailsProperty = typeof(TypeMap).GetProperty("DestinationTypeDetails", BindingFlags.NonPublic | BindingFlags.Instance);
public static void ForAllOtherMembers<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression, Action<IMemberConfigurationExpression<TSource, TDestination, object>> memberOptions)
{
var typeMapConfiguration = (TypeMapConfiguration)expression;
var typeMapActions = (List<Action<TypeMap>>)TypeMapActionsProperty.GetValue(typeMapConfiguration);
typeMapActions.Add(typeMap =>
{
var destinationTypeDetails = (TypeDetails)DestinationTypeDetailsProperty.GetValue(typeMap);
foreach (var accessor in destinationTypeDetails.WriteAccessors.Where(m => typeMapConfiguration.GetDestinationMemberConfiguration(m) == null))
{
expression.ForMember(accessor.Name, memberOptions);
}
});
}
}
MemberList.None 不会阻止具有相同成员名称的自动映射属性。
我还尝试了一些其他解决方案来查找给定配置文件的映射,并将属性映射更改为忽略未映射的属性名称,但这不起作用,因为属性已被视为已映射。
对我来说这个问题的不幸答案是使用 CustomTypeConverter
public class OrderTypeConverter : ITypeConverter<ThirdPartyOrder, MyOrder>
{
public Order.Order Convert(ThirdPartyOrder source, MyOrder destination, ResolutionContext context) =>
new MyOrder()
{
id = source.id,
__type = source.__type,
company_id = source.company_id,
stops = source.stops
};
}
然后
private readonly OrderTypeConverter orderTypeConverter;
public OrderProfile()
{
this.orderTypeConverter = new OrderTypeConverter();
this.CreateMap<ThirdPartyOrder, MyOrder>().ConvertUsing(orderTypeConverter);
}
您可以在 CreateMap 调用中进行设置:
CreateMap<TSource, TDest>(MemberList.None)
这是一个更新的变体,它使用新的 .NET 8 方法来反射私有成员。对于 .NET 8 目标来说,它的性能应该会更高一些。
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using AutoMapper.Configuration;
#if NET8_0_OR_GREATER
using System.Runtime.CompilerServices;
#else
using System.Reflection;
#endif
namespace MyNamespace;
public static class AutoMapperExtensions
{
#if NET8_0_OR_GREATER
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_TypeMapActions")]
private static extern List<Action<TypeMap>> GetTypeMapActions(TypeMapConfiguration typeMapConfiguration);
#else
private static readonly PropertyInfo TypeMapActionsProperty = typeof(TypeMapConfiguration).GetProperty("TypeMapActions", BindingFlags.NonPublic | BindingFlags.Instance)!;
#endif
public static void ForAllOtherMembers<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression, Action<IMemberConfigurationExpression<TSource, TDestination, object>> memberOptions)
{
var typeMapConfiguration = (TypeMapConfiguration)expression;
#if NET8_0_OR_GREATER
var typeMapActions = GetTypeMapActions(typeMapConfiguration);
#else
var typeMapActions = (List<Action<TypeMap>>)TypeMapActionsProperty.GetValue(typeMapConfiguration)!;
#endif
typeMapActions.Add(typeMap =>
{
var destinationTypeDetails = typeMap.DestinationTypeDetails;
foreach (var accessor in destinationTypeDetails.WriteAccessors.Where(m => typeMapConfiguration.GetDestinationMemberConfiguration(m) == null))
{
expression.ForMember(accessor.Name, memberOptions);
}
});
}
}