我在映射中得到
NullReferenceException
,其中源类型具有名为“Original”的属性,并且映射的目标是记录(而不是类)。这是 AutoMapper 版本 12.0.1 的情况。
根据 Lucien 的评论,我添加了对 AssertConfigurationIsValid 的调用,这表明该属性正在映射到构造函数参数。
The following member on AutoMapperIssues.OriginalPropertyMappedToRecordIssue+TypedConstructorDestination cannot be mapped:
Void .ctor(TypedConstructorDestination), parameter original
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type AutoMapperIssues.OriginalPropertyMappedToRecordIssue+TypedConstructorDestination.
Context:
Mapping to member Void .ctor(TypedConstructorDestination), parameter original from AutoMapperIssues.OriginalPropertyMappedToRecordIssue+Source to AutoMapperIssues.OriginalPropertyMappedToRecordIssue+TypedConstructorDestination
Exception of type 'AutoMapper.AutoMapperConfigurationException' was thrown.
在调查时,我尝试定义这样的记录。
private record TypedPrimaryConstructorDestination(TypedPrimaryConstructorDestination Original);
这导致了以下构建错误。
Error CS8910 The primary constructor conflicts with the synthesized copy constructor.
涵盖错误的文档包含以下文本。
将记录修饰符添加到结构或类类型会创建一条记录。记录包含编译器合成的复制构造函数。
该链接指向更多文档,涵盖记录的非破坏性突变。
以下 xUnit 测试类重现了该问题并演示了以下解决方法。进行任何一项更改,就不再引发异常。
using AutoMapper;
namespace AutoMapperIssues
{
public class OriginalPropertyMappedToRecordIssue
{
[Fact]
//This very specific scenario results in a NullReferenceException during mapping.
//It also results in an AutoMapperConfigurationException exception when AssertConfigurationIsValid is called.
//I posted on StackOverflow which is the first step in raising an AutoMapper GitHub issue.
//https://stackoverflow.com/questions/77316677/automapper-throws-nullreferenceexception-with-property-named-original-in-mappi
//It looks like it is the automatically generated (synthesized) copy constructors in records that are at the core of this.
//https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/constructor-errors?f1url=%3FappId%3Droslyn%26k%3Dk(CS8910)#records-and-copy-constructors
public void Reproduction1()
{
Assert.Throws<AutoMapperConfigurationException>(() => TestSourceAndDestinationCombination<Source, Destination>());
}
[Fact]
//Added a constructor with a single parameter named original typed as the destination.
public void Reproduction2()
{
Assert.Throws<AutoMapperConfigurationException>(() => TestSourceAndDestinationCombination<Source, TypedConstructorDestination>());
}
private record TypedConstructorDestination
{
public TypedConstructorDestination(TypedConstructorDestination original)
{
}
}
//[Fact]
//public void Reproduction3()
//{
// TestSourceAndDestinationCombination<Source, TypedPrimaryConstructorDestination>();
//}
//private record TypedPrimaryConstructorDestination(TypedPrimaryConstructorDestination Original);
private static void TestSourceAndDestinationCombination<TSource, TDestination>(Action<IMappingExpression<TSource, TDestination>>? configure = null) where TSource : new()
{
var mapperConfiguration = new MapperConfiguration(
cfg =>
{
var mappingExpression = cfg.CreateMap<TSource, TDestination>();
configure?.Invoke(mappingExpression);
}
);
mapperConfiguration.AssertConfigurationIsValid();
var mapper = mapperConfiguration.CreateMapper();
var source = new TSource();
mapper.Map<TDestination>(source);
}
private record Source { public bool Original { get; init; } }
private record Destination;
[Fact]
//Changed the destination from a record to a class.
public void Workaround1()
{
TestSourceAndDestinationCombination<Source, ClassDestination>();
}
private class ClassDestination { }
[Fact]
//Renamed the property named 'Original' in the source.
public void Workaround2()
{
TestSourceAndDestinationCombination<RenamedPropertySource, Destination>();
}
private record RenamedPropertySource { public bool Original1 { get; init; } }
[Fact]
//Map a null value which has been cast to the destination type to a constructor parameter named 'original'.
public void Workaround3()
{
TestSourceAndDestinationCombination<Source, Destination>(
expression => expression.ForCtorParam(
"original",
options => options.MapFrom(
source => (Destination?)null
)
)
);
}
[Fact]
//Changed the type of the Original property in the source to be the destination type.
public void Workaround4()
{
TestSourceAndDestinationCombination<PropertyTypedAsDestinationSource, Destination>();
}
private record PropertyTypedAsDestinationSource { public Destination Original { get; init; } }
[Fact]
//Added a primary constructor with a single parameter with the same type as the source property.
public void Workaround5()
{
TestSourceAndDestinationCombination<Source, PrimaryConstructorDestination>();
}
private record PrimaryConstructorDestination(bool Original);
[Fact]
//Added a constructor with a single parameter with the same type as the source property.
public void Workaround7()
{
TestSourceAndDestinationCombination<Source, ConstructorDestination>();
}
private record ConstructorDestination{
ConstructorDestination(bool original)
{
}
}
}
}
这对我来说看起来像是一个错误。 AutoMapper GitHub 存储库 中的说明要求在将其作为问题提交之前在 StackOverflow 上进行确认。所以请帮忙确认一下这是一个bug。
如果您是 AutoMapper 新手,请先在 StackOverflow 上提问,如果那里的人认为这是一个错误,请返回此处。
一个简单的解决方案是仅使用公共构造函数:
var configuration = new MapperConfiguration(cfg => cfg.ShouldUseConstructor = constructor => constructor.IsPublic);