Skip to content

v10 no longer uses default values for missing constructor parameters #883

Closed

Description

@Ergamon

Considering these 2 classes:

public class Source
{
  public required string Value1 { get; init; }
}

public class Target
{
  public Target(string value1, string value2)
  {
    Value1 = value1;
    Value2 = value2;
  }

  public string Value1 { get; }

  public string Value2 { get; }
}

Running this code:

    var source = new Source
    {
      Value1 = "123",
    };

    var target = source.Adapt<Target>();

This works in v7.4.0 resulting in a Source instance with Value1 = "123" and Value2 = null.

In v10 I get this exception:
System.InvalidOperationException: No default constructor for type 'Target', please use 'ConstructUsing' or 'MapWith'

Sounds like a pretty huge breaking change in my opinion. I dont see anything about it in the changelog.

Activity

  1. DocSvartz commented on Mar 22, 2026

    @DocSvartz
    Contributor

    Hi @Ergamon , it is related to the point that this class in v10 is not a mapster record type.

    Hm. I thought that this case shouldn't have worked in version 7.4.0 either.
    Because I remember an open issue about this after version 7.4.0 was released;

    @andrerav @stagep @Ergamon

    We can add automatic activation of the class in this case, but there are two problems:

    1. Сlass mapping never supported this behavior;
    2. These will start having problems with MapToTarget Critical regression introduced in Mapster 7.3.0 when mapping to existing objects #537 Since the class mapping pipeline does not support such mapping.
  2. DocSvartz commented on Mar 22, 2026

    @DocSvartz
    Contributor

    @andrerav @stagep There seems to be an error with the implementation of [UseDestinationValue] for primitive value AutoProperty. in v10
    Why does it work even without using it, which probably shouldn't 🤔

    If we accept this behavior as a feature and not a bug😅. we could implement constructor activation for classes with a default value (although there could still be issues with Projection).

    But it seems to me that mutating the value of a property without a setter, without explicitly indicating this, is not a very good idea.

    update: No, this was done exactly for such cases by MapToTarget.

    [TestMethod]
    public void CustomRecordWithConfig_Working_()
    {
        var patch = new Target833("Hello","John");
        var destination = new Target833("Sample text", "Name");
    
        patch.Adapt(destination);
    
        destination.Value1.ShouldBe("Hello");
        destination.Value2.ShouldBe("John");
    }
    
    public class Source
    {
      public required string Value1 { get; init; }
    }
    
    public class Target
    {
      public Target(string value1, string value2)
      {
        Value1 = value1;
        Value2 = value2;
      }
    
      public string Value1 { get; }
      public string Value2 { get; }
    }
    
    
  3. added 2 commits that reference this issue on Mar 23, 2026
    e9b33cf
    a60020b
  4. added a commit that references this issue on Mar 24, 2026
    b4feddb
  5. Ergamon commented on Mar 24, 2026

    @Ergamon
    Author

    I made a quick test with the latest v10 version and it works again.
    Thank you

  6. anaruzhnii commented on Apr 3, 2026

    @anaruzhnii

    We have encountered quite unwanted behavior with Mapster v.10.0.6 which was not present in v.7.4.0.

    When I try to map classes with certain property types, it throws an error. So far I have found the following types that cause the following exceptions in v.10.0.6:

    JsonDocument:

    Unhandled exception. Mapster.CompileException: Error while compiling
    source=SourceClassWithJsonDocument
    destination=DestinationClassWithJsonDocument
    type=Map
     ---> Mapster.CompileException: Error while compiling
    source=System.Text.Json.JsonDocument
    destination=System.Text.Json.JsonDocument
    type=Map
     ---> System.InvalidOperationException: No default constructor for type 'JsonDocument', please use 'ConstructUsing' or 'MapWith'
    
    

    Uri:

    Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'uriString')
       at System.ArgumentNullException.Throw(String paramName)
       at lambda_method1(Closure, Object)
       at Mapster.TypeAdapter.Adapt[TDestination](Object source, TypeAdapterConfig config)
       at Mapster.TypeAdapter.Adapt[TDestination](Object source)
    

    It can be reproduced with the following code:

    using System.Text.Json;
    using Mapster;
    
    var jsonSource = new SourceClassWithJsonDocument
    {
        Json = JsonDocument.Parse("{\"key\": \"value\"}")
    };
    
    var jsonDest = jsonSource.Adapt<DestinationClassWithJsonDocument>();
    Console.WriteLine(jsonDest.Json.RootElement.GetProperty("key").GetString());
    
    var uriSource = new SourceClassWithUri
    {
        Uri = new Uri("https://www.google.com/")
    };
    
    var uriDest = uriSource.Adapt<DestinationClassWithUri>();
    Console.WriteLine(uriDest.Uri);
    
    class SourceClassWithJsonDocument
    {
        public required JsonDocument Json { get; init; }
    }
    
    class DestinationClassWithJsonDocument
    {
        public required JsonDocument Json { get; init; }
    }
    
    class SourceClassWithUri
    {
        public required Uri Uri { get; init; }
    }
    
    class DestinationClassWithUri
    {
        public required Uri Uri { get; init; }
    }

    Downgrading the Mapster version to v.7.4.0 eliminates the problem.

    It also can be fixed with the mapping rules, like:

    TypeAdapterConfig<JsonDocument, JsonDocument>.NewConfig().MapWith(src => src);
    TypeAdapterConfig<Uri, Uri>.NewConfig().MapWith(src => src);

    , but due to the nature of specifying every type in quite large codebase this workaround is not preferred.

    Is there any way to fix it without downgrading?

  7. DocSvartz commented on Apr 3, 2026

    @DocSvartz
    Contributor

    Hi @anaruzhnii Did you expect that after mapping it would be the same object?

    in v 7.4.0

    var uri = new Uri("https://www.google.com/");
    var result = uri.Adapt<Uri>();
    var s = object.ReferenceEquals(uri, result); // true
    
  8. anaruzhnii commented on Apr 3, 2026

    @anaruzhnii

    Hi @DocSvartz, thanks for the quick reply.

    In my use-case source objects are usually disposed after mapping, so the question of equality is irrelevant to me.

    However, getting a variety of exceptions after the upgrade was a surprise for me. Especially considering the fact that exceptions happen only when mapping is called, the upgrade underlies a requirement of additional testing.

    Should this change be inevitable, a log warning and a migration manual would be nice to have.

  9. DocSvartz commented on Apr 3, 2026

    @DocSvartz
    Contributor

    Should this change be inevitable, a log warning and a migration manual would be nice to have.

    I don't think the situation in which this happened in v7.4.0 was entirely correct. These aren't primitive types, after all.
    But whether the result itself is correct is more of a matter of debate.🤔 I'll think about it some more.

    In my use-case source objects are usually disposed after mapping, so the question of equality is irrelevant to me.

    Try using this:

    TypeAdapterConfig.GlobalSettings.Default.ShallowCopyForSameType(true);

    For your examples this will definitely work.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions