Mapster icon indicating copy to clipboard operation
Mapster copied to clipboard

Mapping 2 objects to poco - issue when mapping the other way

Open ChristoGC opened this issue 2 years ago • 2 comments

I thought I would flag an issue I observed.

When following the example in https://github.com/MapsterMapper/Mapster/wiki/Custom-mapping Example 2: Mapping 2 objects to poco there seems to be an issue when reversing the mapping. Creating a new tuple from from a poco is fine, but updates do not work.

Example code

const bool ShowScript = true;

void Main()
{
	TypeAdapterConfig.GlobalSettings.Compiler = exp => exp.CompileWithDebugInfo();
	string script;

	// Tuple to POCO
	TypeAdapterConfig<(AuthorizationGroup authorizationGroup, GroupDetails groupDetails), GroupDM>
		.NewConfig()
			.Map(dest => dest, src => src.authorizationGroup)
			.Map(dest => dest, src => src.groupDetails);


	AuthorizationGroup authorizationGroup = new() { Description = "TEST" };
	GroupDetails groupDetails = new() { GroupId = 10 };

	if(ShowScript)
	{
		script = (authorizationGroup, groupDetails).BuildAdapter()
					.CreateMapExpression<GroupDM>()
					.ToScript();

		Console.WriteLine(script);

		script = (authorizationGroup, groupDetails).BuildAdapter()
					.CreateMapToTargetExpression<GroupDM>()
					.ToScript();

		Console.WriteLine(script);
	}

	// Lets create a new instance of GroupDM: Works
	var obj = (authorizationGroup, groupDetails).Adapt<GroupDM>();

	// Now lets update
	authorizationGroup.Description = "UPDATED TEST";
	groupDetails.GroupId = 50;

	// Now lets update GroupDM: Works
	(authorizationGroup, groupDetails).Adapt(obj);
	
	// POCO to Tuple
	TypeAdapterConfig<GroupDM, (AuthorizationGroup authorizationGroup, GroupDetails groupDetails)>
	.NewConfig()
		.Map(dest => dest.authorizationGroup, src => src)
		.Map(dest => dest.groupDetails, src => src);

	GroupDM groupDM = new() { Name = "Hello world", Description = "Jon Bon Jovi" };

	if (ShowScript)
	{
		script = groupDM.BuildAdapter()
					.CreateMapExpression<(AuthorizationGroup authorizationGroup, GroupDetails groupDetails)>()
					.ToScript();
	
		Console.WriteLine(script);
	
	
		script = groupDM.BuildAdapter()
					.CreateMapToTargetExpression<(AuthorizationGroup authorizationGroup, GroupDetails groupDetails)>()
					.ToScript();

		Console.WriteLine(script);
	}

	// Create a new instance of (AuthorizationGroup, GroupDetails): This works
	var tuple = groupDM.Adapt<(AuthorizationGroup authorizationGroup, GroupDetails groupDetails)>();

	// Now lets update poco
	groupDM.GroupId = 99;
	groupDM.Description = "Led Zep";
	
	//Now lets update tuple: Doesn't work
	groupDM.Adapt(tuple);
}

public class GroupDM 
{
	public int GroupId;
	public string Name { get; set; }
	public string Description { get; set; }
}

public class AuthorizationGroup
{
	public string Name { get; set; }
	public string Description { get; set; }
}

public class GroupDetails
{
	public int GroupId { get; set; }
}

The expression scripts produced suggest that CreateMapToTargetExpression on poco to tuple is incorrect as it is exactly the same as CreateMapExpression

// New: Tuple to POCO
public GroupDM Main(ValueTuple<AuthorizationGroup, GroupDetails> p1)
{
    return new GroupDM()
    {
        Name = p1.Item1.Name,
        Description = p1.Item1.Description,
        GroupId = p1.Item2.GroupId
    };
}

// Update: Tuple to POCO
public GroupDM Main(ValueTuple<AuthorizationGroup, GroupDetails> p1, GroupDM p2)
{
    GroupDM result = p2 ?? new GroupDM();
   
    result.Name = p1.Item1.Name;
    result.Description = p1.Item1.Description;
    result.GroupId = p1.Item2.GroupId;
    return result;
   
}

// New:  POCO to Tuple
public ValueTuple<AuthorizationGroup, GroupDetails> Main(GroupDM p1)
{
    return p1 == null ? default(ValueTuple<AuthorizationGroup, GroupDetails>) : new ValueTuple<AuthorizationGroup, GroupDetails>(p1 == null ? null : new AuthorizationGroup()
    {
        Name = p1.Name,
        Description = p1.Description
    }, p1 == null ? null : new GroupDetails() {GroupId = p1.GroupId});
}

// Update:  POCO to Tuple
public ValueTuple<AuthorizationGroup, GroupDetails> Main(GroupDM p1, ValueTuple<AuthorizationGroup, GroupDetails> p2)
{
    return p1 == null ? default(ValueTuple<AuthorizationGroup, GroupDetails>) : new ValueTuple<AuthorizationGroup, GroupDetails>(p1 == null ? null : new AuthorizationGroup()
    {
        Name = p1.Name,
        Description = p1.Description
    }, p1 == null ? null : new GroupDetails() {GroupId = p1.GroupId});
}

Thanks in advance,

Chris

ChristoGC avatar Jul 04 '23 12:07 ChristoGC

Hello, named tuple, not named before compile. then always == Tuple<type1, type2> with tipe1 item1, type2 item2. You mapping for name, but name Not achievable, including for reflection, and after compile. Name use only when development type helpers

DocSvartz avatar Oct 03 '23 03:10 DocSvartz

Hello @ChristoGC If You get This Result?

[TestMethod]
public void MappedNamedTuple()
{
    TypeAdapterConfig<GroupDM604, (AuthorizationGroup604 authorizationGroup, GroupDetails604 groupDetails)>
    .NewConfig()
    .Map(dest => dest.authorizationGroup, src => src)
    .Map(dest => dest.groupDetails, src => src);

    GroupDM604 groupDM = new() { Name = "Hello world", Description = "Jon Bon Jovi" };

    // Create a new instance of (AuthorizationGroup, GroupDetails): This works
    var tuple = groupDM.Adapt<(AuthorizationGroup604 authorizationGroup, GroupDetails604 groupDetails)>();

    // Now lets update poco
    groupDM.GroupId = 99;
    groupDM.Description = "Led Zep";

    //Now lets update tuple: Doesn't work
    groupDM.Adapt(tuple);

    tuple.groupDetails.GroupId.ShouldBe(99); // true
    tuple.authorizationGroup.Description.ShouldBe("Led Zep"); // true 
}

DocSvartz avatar Oct 21 '23 12:10 DocSvartz