I recently upgraded my codebase from Mapster 9.0.0-pre01 to 10.0.7.
After the upgrade, mappings from an interface DTO to a read-only domain interface fail during config.Compile() when the global configuration has:
config.Default.IgnoreNullValues(true);
The same setup worked in both version 7.4.0 and 9.0.0-pre01.
Repro:
public interface IDomain
{
string Id { get; }
string Value { get; }
IList<string> ValueList { get; }
}
public class DomainDerived : IDomain
{
public string Id { get; set; }
public string Value { get; set; }
public IList<string> ValueList { get; set; }
public DomainDerived()
{
}
public DomainDerived(
string id,
string value,
IList<string> valueList)
{
Id = id;
Value = value;
ValueList = valueList;
}
}
public interface IDto
{
string Id { get; set; }
string Value { get; set; }
IList<string> ValueList { get; set; }
}
public class DtoDerived : IDto
{
public string Id { get; set; }
public string Value { get; set; }
public IList<string> ValueList { get; set; }
}
Configuration:
public static TypeAdapterConfig ConfigureMapster(MappingAssemblyProvider mappingAssemblyProvider)
{
var config = TypeAdapterConfig.GlobalSettings;
config.Default.PreserveReference(true);
config.Default.EnumMappingStrategy(EnumMappingStrategy.ByName);
config.Default.IgnoreNullValues(true);
config.Default.EnableNonPublicMembers(true);
config.Default.MapToConstructor(true);
config.Default.ShallowCopyForSameType(false);
config.AllowImplicitSourceInheritance = true;
config.RequireDestinationMemberSource = true;
foreach (var assembly in mappingAssemblyProvider.GetMapperAssemblies())
{
config.Scan(assembly);
}
config.Compile();
return config;
}
Test:
[TestFixture]
public class RecordMappingTests
{
[SetUp]
public void SetUp()
{
MappingConfiguration.ConfigureMapster(new MappingAssemblyProvider());
}
[Test]
public void DtoBase_Maps_To_DomainBase()
{
var dto = new DtoDerived
{
Id = "id",
Value = "test",
ValueList = new List<string> { "value1", "value2" }
};
var domain = dto.Adapt<IDto, IDomain>();
Assert.That(domain.Id, Is.EqualTo(dto.Id));
Assert.That(domain.Value, Is.EqualTo(dto.Value));
Assert.That(domain.ValueList, Is.EquivalentTo(dto.ValueList));
}
}
Throws the following error on compile:
Mapster.CompileException : Error while compiling
source=Dto.IDto
destination=Domain.IDomain
type=Map
----> System.ArgumentException : Expression must be writeable (Parameter 'left')
I can make a workaround by disabling 'IgnoreNullValues(true)' from the global config. But this introduced several other issues in my codebase (see issue #928). I have also tried to add '.Ignore()' on the base mapping config, but this seems to be auto-inherited to the derived configs, and therefore also breaks the mapping.
Notes
This appears to be related to the interaction between:
IgnoreNullValues(true)
read-only destination interface members
Include<TDerivedSource, TDerivedDestination>()
polymorphic mapping from base interface to base interface
The read-only interface itself cannot be assigned to directly, but the included destination implementation has public setters. I would expect the included mapping to be used for the runtime derived type rather than generating assignments against the read-only base interface members.
I recently upgraded my codebase from Mapster
9.0.0-pre01to10.0.7.After the upgrade, mappings from an interface DTO to a read-only domain interface fail during
config.Compile()when the global configuration has:The same setup worked in both version 7.4.0 and 9.0.0-pre01.
Repro:
Configuration:
Test:
Throws the following error on compile:
I can make a workaround by disabling 'IgnoreNullValues(true)' from the global config. But this introduced several other issues in my codebase (see issue #928). I have also tried to add '.Ignore()' on the base mapping config, but this seems to be auto-inherited to the derived configs, and therefore also breaks the mapping.
Notes
This appears to be related to the interaction between:
IgnoreNullValues(true)
read-only destination interface members
Include<TDerivedSource, TDerivedDestination>()
polymorphic mapping from base interface to base interface
The read-only interface itself cannot be assigned to directly, but the included destination implementation has public setters. I would expect the included mapping to be used for the runtime derived type rather than generating assignments against the read-only base interface members.