You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
RequireExplicitMapping is not working as expected. #783
When converting between two class, both of which have a property named MyProperty with different types (one is string and the other is int),
I have configured RequireExplicitMapping = true.
I expected an exception to occur during the Compile phase, indicating that I should explicitly configure the conversion between these two properties. However, that did not happen. The error only occurs at runtime if the conversion fails.
publicstaticvoidConfigureMappings(){TypeAdapterConfig.GlobalSettings.RequireExplicitMapping=true;TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource=true;ConfigurePatientInfoMappings();// expect an exception hereTypeAdapterConfig.GlobalSettings.Compile();// test codevarinfo=newPatientInformation();info.MyProperty="test";varviewObject=info.Adapt<PatientInfoViewObject>();}privatestaticvoidConfigurePatientInfoMappings(){TypeAdapterConfig<PatientInformation,PatientInfoViewObject>.NewConfig();TypeAdapterConfig<PatientInfoViewObject,PatientInformation>.NewConfig();}
@JasonGrass in 7.4.0 RequireExplicitMapping = true work only for mapping to Class.
You create custom config here private static void ConfigurePatientInfoMappings() That's why it doesn't work.
Int is Struct - apparently this is the main reason why it doesn't work.
This needs some thought, but could be improved.
Here's a case where it works.
And then it really works as you expected
[TestMethod]
public void RequireExplicitMappingWorkFromClass()
{
TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true;
TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true;
ConfigurePatientInfoMappings();
TypeAdapterConfig.GlobalSettings.Compile(); // get Exception- > Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists.
// because not config for mapping MyString to Myint
var info = new Source783();
info.MyProperty.Value = "123";
var viewObject = info.Adapt<Destination783>();
}
private static void ConfigurePatientInfoMappings()
{
TypeAdapterConfig<Source783, Destination783>.NewConfig();
}
public class Source783
{
public MyString MyProperty { get; set; } = new ();
}
public class Destination783
{
public MyInt MyProperty { get; set; } = new ();
}
public class MyString
{
public string Value { get; set; }
}
public class MyInt
{
public int Value { get; set; }
}
or for
public class Source783
{
public int MyProperty { get; set; }
}
public class Destination783
{
public string MyProperty { get; set; } = new ();
}
But the side effect of using it is that the User will need to manually register all configurations used for mapping Mapster Primitive types. (Except for mapping itself to int->int ... and others)
Is this setting up a catch 22 scenario, in that the user needs to know about the setting in order to know to setup primitive to primitive mappings? I use Mapster.Tool so I catch any mapping issues at compile time because I want to avoid mapping exceptions at runtime. Is adding another global setting just setting up the potential for more missing configuration?
requires the user to setup the primitive mappings? If this is the case then should a tooltip be added on this property informing the user that mapping of primitive types is required when this property is set to true. This might be a good opportunity to provide tooltips and enhance the documentation regarding settings that have major changes in how mapping works. A section on Global Settings on the readme would be helpful.
Is this setting up a catch 22 scenario, in that the user needs to know about the setting in order to know to setup primitive to primitive mappings?
I think the bigger pitfall is simply extending the current RequireExplicitMapping action to primitive types.
(As far as I understand, the current tests are made with the condition that this setting does not apply to them).
Also Wiki contains a description that this setting applies only to Classes
I use Mapster.Tool so I catch any mapping issues at compile time because I want to avoid mapping exceptions at runtime. Is adding another global setting just setting up the potential for more missing configuration?
By default, this will always be off RequireExplicitMappingPrimitive = false; and does not change the current behavior.
In the state RequireExplicitMappingPrimitive = true, this will be equivalent to RequireExplicitMapping = true or .IgnoreNonMapped(true). ("Only manual mapping mode" )
In the sense that either manual mapping will be needed
or just manual registration of mapping of a bundle of two primitive types
TypeAdapterConfig<string, int>.NewConfig();
// or
TypeAdapterConfig<Source783, Destination783>.NewConfig()
.Map(dest=>dest.MyProperty,src=> int.Parse(src.MyProperty));
provided that the User really understands why this setting was activated
requires the user to setup the primitive mappings?
And yes, this should be added to the WIKI and a description of the setting in the code.
1 Mapster should only assist me in automatically handling property mappings that have exactly the same name and type.
2 If either the Source or Destination has properties that the other does not, the user needs to manually configure these properties to be ignored; otherwise, an exception should be thrown.
3 If a property exists in both the Source and Destination with the same name but different types (even if it's int and double), the user should manually configure the conversion operation for this property; otherwise, an exception should be thrown.
Rationale for These Considerations
In simple terms: to prevent possible operational errors by other team members.
The fundamental idea is that for all potential sources of error, explicit configuration is required to eliminate any ambiguity.
For example:
1 A property was added in the Source or Destination but was forgotten on the other side.
2 The type of a property in the Source and Destination is inconsistent, while in reality, they should be consistent.
classSource783{// Only automatically handle properties that have the same type and name.publicstringName{get;set;}="";// I expect there to be a property called RequireSourceMember,// which, when set to true, will throw an exception during compilation,// unless the user manually configures to ignore this property.publicstringPropertyOnlyInSource{get;set;}="";publicintValue{get;set;}}classDestination783{// Only automatically handle properties that have the same type and name.publicstringName{get;set;}="";// if RequireDestinationMemberSource = true// Mapster will throw an exception when compile// this is what we wantpublicstringPropertyOnlyInDestination{get;set;}="";publicdoubleValue{get;set;}}publicstaticvoidConfigureMappings(){TypeAdapterConfig.GlobalSettings.RequireExplicitMapping=true;TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource=true;TypeAdapterConfig.GlobalSettings.RequireSourceMember=true;// maybeTypeAdapterConfig.GlobalSettings.RequireExplicitMappingPrimitive=true;// maybeTypeAdapterConfig<Source783,Destination783>.NewConfig();TypeAdapterConfig.GlobalSettings.Compile();}
1 Mapster should only assist me in automatically handling property mappings that have exactly the same name and type.
3 If a property exists in both the Source and Destination with the same name but different types (even if it's int and double), the user should manually configure the conversion operation for this property; otherwise, an exception should be thrown.
Yes, from Primitive type it will work as you intended. RequireExplicitMappingPrimitive = true;
2 If either the Source or Destination has properties that the other does not, the user needs to manually configure these properties to be ignored; otherwise, an exception should be thrown.
This is a topic for a separate discussion, and it already exists.
The main problem here is that the value may be obtained from not the Property of the SourceType at all.
In essence, the source Type itself acts as a context for creating or modifying an Instance of the DestinationType.
Thank you for your detailed and patient response.
As it stands, there doesn't seem to be a particularly elegant way to intercept the "same name but different types" situation.
I look forward to the early release of the RequireExplicitMappingPrimitive configuration.
@JasonGrass and all Users of RequireExplicitMapping
When using RequireExplicitMapping = true , do you expect that mapping a class to itself (result = source.Adapt<SourceType>()) will cause an error?
Does this behavior make practical sense?
These are questions for the subsequent unification of the behavior of RequireExplicitMapping and RequireExplicitMappingPrimitive
in terms of mapping types onto themselves :)
I tend to throw an error because this is indeed a confusing operation.
There's a possibility that the user intends to clone an object instance using this approach.
If Mapster supports cloning functionality, it could provide a message in the exception indicating: “If you want to clone, please use the xxx method.”
@JasonGrass I probably forgot to mark this part as code.
I meant this case similar to this one.
result = _source.Adapt<SourceType>()
or
_source.Adapt(_source2) // where _source and _source2 is Type of SourceType
When during mapping a class instance is mapped to another instance of the same class.
Here is a more realistic example
TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true;
TypeAdapterConfig<Source783, Destination783>.NewConfig();
TypeAdapterConfig.GlobalSettings.Compile(); // get Exception because not config for mapping MyString to MyString
public class Source783
{
public MyString MyProperty { get; set; } = new ();
}
public class Destination783
{
public MyString MyProperty { get; set; } = new ();
}
public class MyString
{
public string Value { get; set; }
}
In this case, the expected effect of object mapping should be a deep copy of the object, so that MyString and MyString are different instances. If this is the case, then no exception need be thrown.
However, if it is simply a reference assignment, I believe an exception should be thrown, with an error message explaining the reason: the default behavior is reference assignment, not deep copy.
Users should have the option in their custom mapping to decide whether to use reference assignment or deep copy.
@JasonGrass I see what you mean. But that's outside the scope of RequireExplicitMapping.
It was done to check if the mapping configuration was registered.
In this case, the expected effect of object mapping should be a deep copy of the object, so that MyString and MyString are different instances. If this is the case, then no exception need be thrown.
However, if it is simply a reference assignment, I believe an exception should be thrown, with an error message explaining the reason: the default behavior is reference assignment, not deep copy.
If you are interested in this, open a discussion on adding this feature.
Activity
DocSvartz commentedon Mar 20, 2025
@JasonGrass in 7.4.0
RequireExplicitMapping = truework only for mapping to Class.You create custom config here
private static void ConfigurePatientInfoMappings()That's why it doesn't work.Int is Struct - apparently this is the main reason why it doesn't work.
This needs some thought, but could be improved.
Here's a case where it works.
And then it really works as you expected
or for
DocSvartz commentedon Mar 20, 2025
@JasonGrass If your goal is to make sure that all properties are mapping, then you can use the following config
But then you have to manually map all the Properties.
But as I understand it, this is not quite what you need?
DocSvartz commentedon Mar 20, 2025
@andrerav @stagep can add the following setting:
TypeAdapterConfig.GlobalSettings.RequireExplicitMappingPrimitive = true;But the side effect of using it is that the User will need to manually register all configurations used for mapping Mapster Primitive types. (Except for mapping itself to int->int ... and others)
What do you think about this?
stagep commentedon Mar 20, 2025
Is this setting up a catch 22 scenario, in that the user needs to know about the setting in order to know to setup primitive to primitive mappings? I use Mapster.Tool so I catch any mapping issues at compile time because I want to avoid mapping exceptions at runtime. Is adding another global setting just setting up the potential for more missing configuration?
Does using
requires the user to setup the primitive mappings? If this is the case then should a tooltip be added on this property informing the user that mapping of primitive types is required when this property is set to true. This might be a good opportunity to provide tooltips and enhance the documentation regarding settings that have major changes in how mapping works. A section on Global Settings on the readme would be helpful.
DocSvartz commentedon Mar 21, 2025
@stagep
I think the bigger pitfall is simply extending the current RequireExplicitMapping action to primitive types.
(As far as I understand, the current tests are made with the condition that this setting does not apply to them).
Also Wiki contains a description that this setting applies only to Classes
By default, this will always be off RequireExplicitMappingPrimitive = false; and does not change the current behavior.
In the state RequireExplicitMappingPrimitive = true, this will be equivalent to RequireExplicitMapping = true or .IgnoreNonMapped(true). ("Only manual mapping mode" )
In the sense that either manual mapping will be needed
or just manual registration of mapping of a bundle of two primitive types
provided that the User really understands why this setting was activated
And yes, this should be added to the WIKI and a description of the setting in the code.
JasonGrass commentedon Mar 21, 2025
My initial requirements and related ideas
Initial Requirements:
1 Mapster should only assist me in automatically handling property mappings that have exactly the same name and type.
2 If either the Source or Destination has properties that the other does not, the user needs to manually configure these properties to be ignored; otherwise, an exception should be thrown.
3 If a property exists in both the Source and Destination with the same name but different types (even if it's int and double), the user should manually configure the conversion operation for this property; otherwise, an exception should be thrown.
Rationale for These Considerations
In simple terms: to prevent possible operational errors by other team members.
The fundamental idea is that for all potential sources of error, explicit configuration is required to eliminate any ambiguity.
For example:
1 A property was added in the Source or Destination but was forgotten on the other side.
2 The type of a property in the Source and Destination is inconsistent, while in reality, they should be consistent.
DocSvartz commentedon Mar 21, 2025
@JasonGrass
Yes, from Primitive type it will work as you intended. RequireExplicitMappingPrimitive = true;
The main problem here is that the value may be obtained from not the Property of the SourceType at all.
In essence, the source Type itself acts as a context for creating or modifying an Instance of the DestinationType.
Therefore, at the moment there is only a check for the presence of sources for all Members of the DestinationType.
TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true;Perhaps Validation will help you? This is included in the latest pre-releases
JasonGrass commentedon Mar 21, 2025
Thank you for your detailed and patient response.
As it stands, there doesn't seem to be a particularly elegant way to intercept the "same name but different types" situation.
I look forward to the early release of the RequireExplicitMappingPrimitive configuration.
DocSvartz commentedon Mar 26, 2025
@JasonGrass and all Users of RequireExplicitMapping
When using RequireExplicitMapping = true , do you expect that mapping a class to itself
(result = source.Adapt<SourceType>())will cause an error?Does this behavior make practical sense?
These are questions for the subsequent unification of the behavior of RequireExplicitMapping and RequireExplicitMappingPrimitive
in terms of mapping types onto themselves :)
JasonGrass commentedon Mar 27, 2025
I tend to throw an error because this is indeed a confusing operation.
There's a possibility that the user intends to clone an object instance using this approach.
If Mapster supports cloning functionality, it could provide a message in the exception indicating: “If you want to clone, please use the xxx method.”
DocSvartz commentedon Mar 27, 2025
@JasonGrass I probably forgot to mark this part as code.
I meant this case similar to this one.
When during mapping a class instance is mapped to another instance of the same class.
Here is a more realistic example
JasonGrass commentedon Mar 27, 2025
In this case, the expected effect of object mapping should be a deep copy of the object, so that MyString and MyString are different instances. If this is the case, then no exception need be thrown.
However, if it is simply a reference assignment, I believe an exception should be thrown, with an error message explaining the reason: the default behavior is reference assignment, not deep copy.
Users should have the option in their custom mapping to decide whether to use reference assignment or deep copy.
DocSvartz commentedon Mar 27, 2025
@JasonGrass I see what you mean. But that's outside the scope of RequireExplicitMapping.
It was done to check if the mapping configuration was registered.
If you are interested in this, open a discussion on adding this feature.
JasonGrass commentedon Mar 28, 2025
Yes, I agree that this's outside the scope of RequireExplicitMapping, and it should be handled by another configuration.
DocSvartz commentedon Mar 19, 2026
This should be fixed in new release versions.
If your issue is still active, please reopen it or create new issue. Thanks.