为什么在使用记录时必须忽略 AutoMapper 中的所有其他属性?
Why do I have to Ignore all other properties in AutoMapper when using records?
public record Destination(double X, double Y);
public struct Source
{
public double X { get; set; }
public Potato Potato { get; set; }
public double Z { get; set; }
}
public struct Potato
{
public double Y { get; set; }
}
public MappingProfile()
{
CreateMap<Source, Destination>();
.ForCtorParam(nameof(Destination.Y), e => e.MapFrom(x => x.Potato.Y))
.ForAllOtherMembers(x => x.Ignore());
}
要将源映射到目标,我需要手动映射其中一个子项。
然而,automapper 会给出一个极其混乱的消息,说 Y 属性 未映射。
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
==========================================================================================================
Source -> Destination (Destination member list)
Unmapped properties:
Y
我发现通过添加忽略所有其他成员的行,它会 'solve' 问题。有没有更好的方法来防止这个错误的发生?
报错信息提到映射所有构造函数参数,但即使我添加.ForCtorParam(nameof(Destination.X), e => e.MapFrom(x => x.X))
错误仍然出现。
这已经在 11 中解决了。因为您无法升级,所以您将不得不忽略所有这些属性。问题是属性有设置器,在 AM 10 中,属性不被视为映射,即使它们已经通过构造函数映射。
另一个解决方案是使用不带 setter 的结构(或 class)而不是 record
。
我创建了以下 class 来帮助满足我的需求,因为我无法升级到更新的 AutoMapper 版本。
这很天真,并假定记录上的所有属性都应映射到构造函数。
好处:
- 将自动映射同名的所有属性
- 可以使用 lambda 参数代替
nameof
- 不需要调用忽略其他成员
- 如果 属性 未映射
,将抛出异常
要使用它,在 MappingProfile
:
new RecordMapBuilder<TSource, TDestination>(this)
.Map(x => x.Foo, x => x.Offset.Foo)
.Build();
public class RecordMapBuilder<TSource, TDestination>
{
private readonly Profile _profile;
private readonly bool _autoMapMatchingProperties;
private readonly Dictionary<string, Expression<Func<TSource, object>>> _ctorParamActions = new();
public RecordMapBuilder(Profile profile, bool autoMapMatchingProperties = true)
{
_profile = profile;
_autoMapMatchingProperties = autoMapMatchingProperties;
}
public void Build()
{
var map = _profile.CreateMap<TSource, TDestination>();
var unmappedDestinationProperties = new HashSet<string>(
typeof(TDestination)
.GetProperties()
.Where(e => !_ctorParamActions.ContainsKey(e.Name))
.Select(e => e.Name));
var sourceProperties = new HashSet<string>(typeof(TSource)
.GetProperties()
.Select(e => e.Name));
var mappableProperties = unmappedDestinationProperties.Intersect(sourceProperties).ToHashSet();
var unMappableProperties = unmappedDestinationProperties.Except(sourceProperties).ToHashSet();
if (unMappableProperties.Any())
{
var properties = string.Join(", ", unMappableProperties);
throw new InvalidOperationException($"Not all properties mapped for type {typeof(TSource)} -> {typeof(TDestination)}: {properties}");
}
if (_autoMapMatchingProperties)
{
foreach (var name in mappableProperties)
{
map.ForCtorParam(name, x => x.MapFrom(name));
}
}
foreach (var kv in _ctorParamActions)
{
map.ForCtorParam(kv.Key, x => x.MapFrom(kv.Value));
}
map.ForAllOtherMembers(x => x.Ignore());
}
public RecordMapBuilder<TSource, TDestination> Map(Expression<Func<TDestination, object>> destination, Expression<Func<TSource, object>> source)
{
var name = GetName(destination);
_ctorParamActions[name] = source;
return this;
}
private static string GetName(Expression<Func<TDestination, object>> destination)
{
{
if (destination.Body is UnaryExpression ue && ue.Operand is MemberExpression me)
{
return me.Member.Name;
}
}
{
if (destination.Body is MemberExpression me)
{
return me.Member.Name;
}
}
throw new InvalidOperationException($"Unhandled expression of type: {destination.Body.GetType()}");
}
public record Destination(double X, double Y);
public struct Source
{
public double X { get; set; }
public Potato Potato { get; set; }
public double Z { get; set; }
}
public struct Potato
{
public double Y { get; set; }
}
public MappingProfile()
{
CreateMap<Source, Destination>();
.ForCtorParam(nameof(Destination.Y), e => e.MapFrom(x => x.Potato.Y))
.ForAllOtherMembers(x => x.Ignore());
}
要将源映射到目标,我需要手动映射其中一个子项。 然而,automapper 会给出一个极其混乱的消息,说 Y 属性 未映射。
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
==========================================================================================================
Source -> Destination (Destination member list)
Unmapped properties:
Y
我发现通过添加忽略所有其他成员的行,它会 'solve' 问题。有没有更好的方法来防止这个错误的发生?
报错信息提到映射所有构造函数参数,但即使我添加.ForCtorParam(nameof(Destination.X), e => e.MapFrom(x => x.X))
错误仍然出现。
这已经在 11 中解决了。因为您无法升级,所以您将不得不忽略所有这些属性。问题是属性有设置器,在 AM 10 中,属性不被视为映射,即使它们已经通过构造函数映射。
另一个解决方案是使用不带 setter 的结构(或 class)而不是 record
。
我创建了以下 class 来帮助满足我的需求,因为我无法升级到更新的 AutoMapper 版本。
这很天真,并假定记录上的所有属性都应映射到构造函数。
好处:
- 将自动映射同名的所有属性
- 可以使用 lambda 参数代替
nameof
- 不需要调用忽略其他成员
- 如果 属性 未映射 ,将抛出异常
要使用它,在 MappingProfile
:
new RecordMapBuilder<TSource, TDestination>(this)
.Map(x => x.Foo, x => x.Offset.Foo)
.Build();
public class RecordMapBuilder<TSource, TDestination>
{
private readonly Profile _profile;
private readonly bool _autoMapMatchingProperties;
private readonly Dictionary<string, Expression<Func<TSource, object>>> _ctorParamActions = new();
public RecordMapBuilder(Profile profile, bool autoMapMatchingProperties = true)
{
_profile = profile;
_autoMapMatchingProperties = autoMapMatchingProperties;
}
public void Build()
{
var map = _profile.CreateMap<TSource, TDestination>();
var unmappedDestinationProperties = new HashSet<string>(
typeof(TDestination)
.GetProperties()
.Where(e => !_ctorParamActions.ContainsKey(e.Name))
.Select(e => e.Name));
var sourceProperties = new HashSet<string>(typeof(TSource)
.GetProperties()
.Select(e => e.Name));
var mappableProperties = unmappedDestinationProperties.Intersect(sourceProperties).ToHashSet();
var unMappableProperties = unmappedDestinationProperties.Except(sourceProperties).ToHashSet();
if (unMappableProperties.Any())
{
var properties = string.Join(", ", unMappableProperties);
throw new InvalidOperationException($"Not all properties mapped for type {typeof(TSource)} -> {typeof(TDestination)}: {properties}");
}
if (_autoMapMatchingProperties)
{
foreach (var name in mappableProperties)
{
map.ForCtorParam(name, x => x.MapFrom(name));
}
}
foreach (var kv in _ctorParamActions)
{
map.ForCtorParam(kv.Key, x => x.MapFrom(kv.Value));
}
map.ForAllOtherMembers(x => x.Ignore());
}
public RecordMapBuilder<TSource, TDestination> Map(Expression<Func<TDestination, object>> destination, Expression<Func<TSource, object>> source)
{
var name = GetName(destination);
_ctorParamActions[name] = source;
return this;
}
private static string GetName(Expression<Func<TDestination, object>> destination)
{
{
if (destination.Body is UnaryExpression ue && ue.Operand is MemberExpression me)
{
return me.Member.Name;
}
}
{
if (destination.Body is MemberExpression me)
{
return me.Member.Name;
}
}
throw new InvalidOperationException($"Unhandled expression of type: {destination.Body.GetType()}");
}