Cannot map properties that have differt names and init access modifier
Hi,
I have a problem with version 7.4. If I want to map two properties with different names and the destination has a different name and the init modifier, Mapster throws a NullReferenceException. This happens when in the configuration of Mapster I set PreserveReference to true.
Here's the code
{
public Mapper Mapper { get; }
public MapsterTest()
{
var config = new TypeAdapterConfig();
config.Default.Settings.PreserveReference = true;
config.Scan(Assembly.GetAssembly(typeof(MapsterTest)));
config.Compile();
Mapper = new Mapper(config);
}
}
internal class MyClass
{
public int MyProperty { get; set; }
}
internal class DtoClass
{
public int MyPropertyDto { get; init; }
}
internal class Register : IRegister
{
void IRegister.Register(TypeAdapterConfig config)
{
config.NewConfig<MyClass, DtoClass>()
.Map(x => x.MyPropertyDto, x=> x.MyProperty);
}
}
And here's the mapping
var from = new MyClass { MyProperty = 1};
var to = mapster.Mapper.Map<DtoClass>(from);
(The problem does not manifest if the properties have the same name or another modifier (set; or private set;)
This code works fine with version 7.3
linked #422
@nicoarm93 @andrerav location of this bug found )
PR with fix is open #658
In my case similar regression between 7.4.0 and 7.3.0 (Mapster throws a NullReferenceException) is not related to PreserveReference. It's about init only properties, when mapping to target object.
Below code works fine with Mapster 7.3.0, but doesn't work with Mapster 7.4.0.
using Mapster;
TypeAdapterConfig<Core.RootSource.NestedSource, Core.RootDestination.NestedDestination>
.NewConfig()
.Map(dest => dest.NameDest, src => src.NameSrc);
var dest = new Core.RootDestination { MyProperty = null };
var mapped = Core.RootSource.Create().Adapt(dest);
Console.WriteLine(mapped);
namespace Core
{
public class RootSource
{
public required NestedSource? MyProperty { get; set; }
public static RootSource Create()
{
return new RootSource
{
MyProperty = new NestedSource
{
NameSrc = "NotEmptyString"
}
};
}
public class NestedSource
{
public required string NameSrc { get; set; }
}
}
public class RootDestination
{
public NestedDestination? MyProperty { get; set; }
public override string ToString()
{
return $"{nameof(RootDestination)}.{nameof(MyProperty)}={MyProperty?.NameDest}";
}
public class NestedDestination
{
public string? NameDest { get; init; }
}
}
}
Hello @Mat3oo, @andrerav I checked my fix. The bug indicated by @Mat3oo persists. The place of origin itself is most likely identified correctly. But the created working code and the created non-working code, at first glance, do not make sense and both should not work))
Working Code :
.Lambda #Lambda1<System.Func`3[Core.RootSource+NestedSource,Core.RootDestination+NestedDestination,Core.RootDestination+NestedDestination]>(
Core.RootSource+NestedSource $var1,
Core.RootDestination+NestedDestination $var2) {
.Block(Core.RootDestination+NestedDestination $result) {
.If ($var1 == null) {
.Return #Label1 { null }
} .Else {
.Default(System.Void)
};
$result = ($var2 ?? .New Core.RootDestination+NestedDestination());
.Block() {
$result.NameDest = $var1.NameSrc
};
.Return #Label1 { $result };
.Label
null
.LabelTarget #Label1:
}
}
Non-working code (throws a NullReferenceException):
.Lambda #Lambda1<System.Func`3[Core.RootSource+NestedSource,Core.RootDestination+NestedDestination,Core.RootDestination+NestedDestination]>(
Core.RootSource+NestedSource $var1,
Core.RootDestination+NestedDestination $var2) {
.Block(Core.RootDestination+NestedDestination $result) {
.If ($var1 == null) {
.Return #Label1 { null }
} .Else {
.Default(System.Void)
};
$result = ($var2 ?? .New Core.RootDestination+NestedDestination());
.Block() {
.Call (.Call .Constant<System.RuntimeType>(Core.RootDestination+NestedDestination).GetProperty("NameSrc")).SetValue(
$result,
$var1.NameSrc)
};
.Return #Label1 { $result };
.Label
null
.LabelTarget #Label1:
}
}