MapsterMapper / Mapster

A fast, fun and stimulating object to object Mapper
MIT License
4.31k stars 328 forks source link

Using deep destination property mapping with RequireDestinationMemberSource gives unexpected results #434

Open tiger31 opened 2 years ago

tiger31 commented 2 years ago

net6.0 Mapster version: 7.3.0

Lets imagine we have an API with some endpoints returns PocoChildModel and some PocoModel:

class PocoModel
{
    public string A { get; set; }
    public string C { get; set; }
    public PocoChildModel Child { get; set; }
}

class PocoChildModel
{
    public string B { get; set; }
}

And we transform it in following entities

class Poco
{
    public string A { get; set; }
    public PocoChild Child { get; set; }
}

class PocoChild
{
    public string B { get; set; }
    public string C { get; set; }
}

If we use endpoints that return PocoChildModel then PocoChild.C is not required thus ignored. But when working with endpoints that return Poco then PocoChild.C must be mapped from PocoMode.C

Mapping looks like:

config.ForType<PocoModel, Poco>()
    .Map(d => d.A, s => s.A)
    .Map(d => d.Child, s => s.Child)
    .Map(d => d.Child.C, s => s.C);

config.ForType<PocoChildModel, PocoChild>()
    .Map(d => d.B, s => s.B)
    .Ignore(d => d.C);

Mapping PocoChildModel -> PocoChild works as intended, but when mapping PocoModel -> Poco, Poco.Child.C is always null. This behavior is the result of .Ignore(d => d.C), and the call to Ignore can't be skipped because of RequireDestinationMemberSource configuration.

This is a real case but models are simplified

Code example to reproduce:

using Mapster;
using MapsterMapper;

var config = TypeAdapterConfig.GlobalSettings.Clone();
config.RequireDestinationMemberSource = true;

config.ForType<PocoModel, Poco>()
    .Map(d => d.A, s => s.A)
    .Map(d => d.Child, s => s.Child)
    .Map(d => d.Child.C, s => s.C);

config.ForType<PocoChildModel, PocoChild>()
    .Map(d => d.B, s => s.B)
    .Ignore(d => d.C);

config.Compile();

var mapper = new Mapper(config);

var fooModel = mapper.Map<Poco>(new PocoModel
{
    A = "foo",
    C = "baz",
    Child = new PocoChildModel
    {
        B = "bar",
    }
});

if (fooModel.Child.C == null)
{
    throw new Exception();
}

class PocoModel
{
    public string A { get; set; }

    public string C { get; set; }

    public PocoChildModel Child { get; set; }
}

class PocoChildModel
{
    public string B { get; set; }
}

class Poco
{
    public string A { get; set; }
    public PocoChild Child { get; set; }
}

class PocoChild
{
    public string B { get; set; }
    public string C { get; set; }
}