Mapster
Mapster copied to clipboard
Interface Property not mapping
Hello
I love the lightweight powerful mapper however I am struggling with one element of the mapping capability.
Class
public class TypeA { ITypeB TypeB {get;set;} }
Mapping
var destination = source.Adapt<TSource, TDestination>();
Result
public class TypeA { GeneratedType_1 TypeB {get;set;} }
- I have a class that implements ITypeB.
Any help would be appreciated.
Further to this, this is the class that registers my configs.
Method to Register
public static void RegisterMapster(IServiceCollection serviceCollection) {
var externalModelsAssemblyTypes = Assembly.Load("Project.Models.Persistence.External").DetectTypesToRegisterAndMatchToInterface();
var externalSharpModelsAssemblyTypes = Assembly.Load("ExternalSharp").DetectTypesToRegisterAndMatchOnName(externalModelsAssemblyTypes.Keys);
foreach (var externalModelsAssemblyType in externalModelsAssemblyTypes)
{
TypeAdapterConfig.GlobalSettings.ForType(externalModelsAssemblyType.Key, externalModelsAssemblyType.Value).PreserveReference(true);
TypeAdapterConfig.GlobalSettings.ForType(externalModelsAssemblyType.Value, externalModelsAssemblyType.Key).PreserveReference(true);
}
foreach (var externalSharpModelsAssemblyType in externalSharpModelsAssemblyTypes)
{
TypeAdapterConfig.GlobalSettings.ForType(externalSharpModelsAssemblyType.Value, externalSharpModelsAssemblyType.Key).PreserveReference(true);
TypeAdapterConfig.GlobalSettings.ForType(externalSharpModelsAssemblyType.Key, externalSharpModelsAssemblyType.Value).PreserveReference(true);
}
TypeAdapterConfig<ExternalSharp.Customer, Customer>.NewConfig().Map(x => x.ExternalObjectId, x => x.Id).Ignore(destinationMember => destinationMember.Id).TwoWays();
TypeAdapterConfig<ExternalSharp.Order, Order>.NewConfig().Map(x => x.ExternalObjectId, x => x.Id).Ignore(destinationMember => destinationMember.Id).TwoWays();
TypeAdapterConfig<ExternalSharp.Product, Product>.NewConfig().Map(x => x.ExternalObjectId, x => x.Id).Ignore(destinationMember => destinationMember.Id).TwoWays();
TypeAdapterConfig<ExternalSharp.Address, IAddress>.NewConfig().Map(x => x.ExternalObjectId, x => x.Id).TwoWays();
}
@aicukltd It seems your example uses dynamic assembly loading, and so it is difficult to tell if the problem you are seeing is related to that or to a potential bug in Mapster. Can you try making a simple reproducible example without dynamic assembly loading?
The problem is when target propety is an interface. Mapster always generates some proxy type for such a property, even when the value implements this interface.
In the following test case, the first assertion is OK, InterfaceProp.Value
has correct value. But the second assertion fails, because the value of this property is not ValueData
type, but some generated proxy type for interface IValueData
.
If the target property is of interface type and source value implements this interface, this value should be just assigned to the property without doing anything else. The problem is more obvious, if the value is not just a simple POCO, but a class with some business logic (methods). Now all such a methods throws NotImplementedException
.
[TestMethod]
public void MapToInterface()
{
TypeAdapterConfig<SourceDto, TargetDto>.NewConfig()
.Map(dest => dest.InterfaceProp, src => new ValueData { Value = src.DataProp });
SourceDto source = new SourceDto { DataProp = "Dolor" };
TargetDto target = source.Adapt<TargetDto>();
target.ShouldNotBeNull();
target.ShouldSatisfyAllConditions(
() => target.InterfaceProp.Value.ShouldBe("Dolor"),
() => target.InterfaceProp.ShouldBeOfType<ValueData>()
);
// This throws NotImplementedException.
target.InterfaceProp.DoWork();
}
public interface IValueData
{
string Value { get; set; }
void DoWork();
}
public class ValueData : IValueData
{
public string Value { get; set; }
public void DoWork() { }
}
public class TargetDto
{
public IValueData InterfaceProp { get; set; }
}
public class SourceDto
{
public string DataProp { get; set; }
}
Well I managed to make this work. I need to specify explicit mapping configuration between that target interface type and used source type:
TypeAdapterConfig<string, IValueData>.NewConfig()
.MapWith(src => new ValueData { Value = src });
But it would be nice to have MapWith
method for specific destination properties, not for all properties of some type. Something like:
TypeAdapterConfig<SourceDto, TargetDto>.NewConfig()
.MapWith(dest => dest.InterfaceProp, src => OurCustomLogicToCreateInstance(src));
@satano Sorry for the late reply. Unfortunately we can't support this scenario due to code generation limitations. Glad you figured it out :)
you can also ignore mapping that property and use AfterMapping to overcome this problem.
TypeAdapterConfig<SourceDto, TargetDto>.NewConfig().Ignore(dest => dest.InterfaceProp).AfterMapping((src, dest) => dest.InterfaceProp, src => new ValueData { Value = src.DataProp });