Skip to content

Cannot map properties that have differt names and init access modifier #656

Closed

Description

@nicoarm93

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

Activity

  1. DocSvartz commented on Nov 12, 2023

    @DocSvartz
    Contributor

    linked #422

    @nicoarm93 @andrerav location of this bug found )

    PR with fix is open #658

  2. Mat3oo commented on Dec 8, 2023

    @Mat3oo

    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; }
            }
        }
    }
  3. DocSvartz commented on Dec 8, 2023

    @DocSvartz
    Contributor

    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:
        }
    }
    
  4. added a commit that references this issue on Dec 15, 2023
    ca6bf7d
  5. added a commit that references this issue on Jan 2, 2025
    d462da3
  6. stagep commented on Jan 7, 2025

    @stagep

    In order to identify issues that are still active, we are closing issues that we believe are either resolved or are dormant. If your issue is still active then please reopen. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions