user2173353
user2173353

Reputation: 4680

Mapster - Can I use mapping with constructor and default values for ignored properties?

I try to map from class A to class B, where B is missing a property of A, using Mapster. Usually, you do this with .Ignore(e => e.Property) but I use a non-default constructor for initialization (because I need to validate the incoming data within the constructor) and this fails.

Here is a demo code:

using Mapster;
using System.Reflection;

var config = new TypeAdapterConfig()
{
    RequireExplicitMapping = false,
    RequireDestinationMemberSource = true,
};

config.Default
    .PreserveReference(true)
    .MapToConstructor(true)
    .Unflattening(false)
    .IgnoreNullValues(false)
    .NameMatchingStrategy(new NameMatchingStrategy
    {
        SourceMemberNameConverter = input => input.ToLowerInvariant(),
        DestinationMemberNameConverter = input => input.ToLowerInvariant(),
    })
    .ShallowCopyForSameType(false);

var ctorToUse = GetConstructor<B>();
config
    .NewConfig<A, B>()
    .Ignore(e => e.Id) // <--------------- The code fails with or without this invocation.
    .MapToConstructor(ctorToUse);

var mapper = new MapsterMapper.Mapper(config);

static ConstructorInfo? GetConstructor<TDestination>()
{
    var parameterlessCtorInfo = typeof(TDestination).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, new Type[0]);

    var ctors = typeof(TDestination).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
    var validCandidateCtors = ctors.Except(new[] { parameterlessCtorInfo }).ToArray();
    var ctorToUse = validCandidateCtors.Length == 1
        ? validCandidateCtors.First()
        : validCandidateCtors.OrderByDescending(c => c.GetParameters().Length).First();

    return ctorToUse;
}


var a = new A
{
    Text = "test",
};

var docKind = mapper.Map<B>(a); // <------ This fails.

class A
{
    public string? Text { get; set; }
}

class B
{
    public int Id { get; private set; }
    public string Text { get; private set; }

    public B(int id, string text)
    {
        Id = id;
        Text = text;
    }
}

Notice the the code .Ignore(e => e.Id). Including it causes a wrong number of arguments to be sent to the constructor (it omits Id) and it generates and error. Omitting it, generates an error related to the Id property not being mapped.

Is there any way for me to make it use the default Id value (0) instead? I know I can use .MapWith(a => new B(0, a.Text)) to manually guide Mapster on how to do the mapping and this would solve the problem, but I am trying to keep things working in a more automated form.

I would expect having an Ignore() method like this one, but I see no available option:

config
    .NewConfig<A, B>()
    .Ignore(e => e.Id, 0) // Use value of 0 for id
    .Ignore(e => e.Id2, 0) // Use value of 0 for id2
    .MapToConstructor(ctorToUse);

Upvotes: 0

Views: 259

Answers (0)

Related Questions