0

Added a Fiddler: https://dotnetfiddle.net/RgcDUF

{
    public static void Main()
    {
        Console.WriteLine("Hello World");
        TypeAdapterConfig<B, ADto>.NewConfig().Map(dest => dest.Id, src => src.Id);
        var b = new B(){Id = 1};
        var aDto = b.Adapt<ADto>();
        Console.WriteLine(aDto.Id.ToString());
    }
}
public class A{
    public virtual long Id {get;set;}   
}
public class B : A{
    public new int Id {get;set;}
}

public class ADto{
    public int Id {get; set;}
}

Because we hide Id in B, when mapping B to ADto, Mapster gives ambiguous mapping found:

source=B
destination=ADto
type=Map
 ---> System.Reflection.AmbiguousMatchException: Ambiguous match found.
   at System.RuntimeType.GetPropertyImpl(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
   at System.Type.GetProperty(String name, BindingFlags bindingAttr)
   at System.Linq.Expressions.Expression.PropertyOrField(Expression expression, String propertyOrFieldName)
   at Mapster.Utils.ExpressionEx.PropertyOrField(Expression expr, String prop)
   at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
   at Mapster.Utils.ExpressionEx.PropertyOrFieldPath(Expression expr, String path)
   at Mapster.Models.InvokerModel.GetInvokingExpression(Expression exp, MapType mapType)
   at Mapster.ValueAccessingStrategy.CustomResolverFn(Expression source, IMemberModel destinationMember, CompileArgument arg)
   at Mapster.Adapters.BaseClassAdapter.<>c__DisplayClass4_1.<CreateClassConverter>b__2(Func`4 fn, Expression src)
   at System.Linq.Enumerable.SelectManyIterator[TSource,TCollection,TResult](IEnumerable`1 source, Func`2 collectionSelector, Func`3 resultSelector)+MoveNext()
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at Mapster.Adapters.BaseClassAdapter.CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg, Expression destination)
   at Mapster.Adapters.ClassAdapter.CreateInlineExpression(Expression source, CompileArgument arg)
   at Mapster.Adapters.BaseAdapter.CreateInlineExpressionBody(Expression source, CompileArgument arg)
   at Mapster.Adapters.BaseAdapter.CreateExpressionBody(Expression source, Expression destination, CompileArgument arg)
   at Mapster.Adapters.BaseAdapter.CreateAdaptFunc(CompileArgument arg)
   at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
   --- End of inner exception stack trace ---
   at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
   at Mapster.TypeAdapterConfig.CreateMapExpression(TypeTuple tuple, MapType mapType)
   at Mapster.TypeAdapterConfig.CreateDynamicMapExpression(TypeTuple tuple)
   at Mapster.TypeAdapterConfig.<GetDynamicMapFunction>b__66_0[TDestination](TypeTuple tuple)
   at Mapster.TypeAdapterConfig.<>c__DisplayClass55_0`1.<AddToHash>b__0(TypeTuple types)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Mapster.TypeAdapterConfig.AddToHash[T](ConcurrentDictionary`2 hash, TypeTuple key, Func`2 func)
   at Mapster.TypeAdapterConfig.GetDynamicMapFunction[TDestination](Type sourceType)
   at Mapster.TypeAdapter.Adapt[TDestination](Object source, TypeAdapterConfig config)
   at Mapster.TypeAdapter.Adapt[TDestination](Object source)
   at Program.Main()
Command terminated by signal 6

I know this is not excactly pretty code, but need this to work at the moment before I can clean up the code. Is there a setting to make Mapster just accept that it should just look at Id and not peek underneath the hood of B.

ruffen
  • 1,695
  • 2
  • 25
  • 51
  • Rather than `public new int Id {get;set;}` did you mean `public override long Id{get;set;}`? With your class as-is `B` has effectively 2 properties with the same name, thus it's going to be ambiguous and probably not do what you wanted it to – Luke Briggs Oct 28 '22 at 18:15
  • I know, the intention is to hide the property. As I stated, its not pretty... but its what I have to work with. Just found a workaround by using .ToString() in the mapping configuration, seems like mapster ignores the implementation then. – ruffen Oct 28 '22 at 18:18

1 Answers1

0

Found a workaround (not pretty)

using Mapster;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
        TypeAdapterConfig<B, ADto>.NewConfig()
           .Map(dest => dest.Id, src => int.Parse(src.GetId()));
        var b = new B(){Id = 1};
        var aDto = b.Adapt<ADto>();
        Console.WriteLine(aDto.Id.ToString());
    }
}
public class A{
    public virtual long Id {get;set;}   
}
public class B : A{
    public new int Id {get;set;}
    public int GetId(){
        return Id;
    }

}

public class ADto{
    public int Id {get; set;}
}

Essentially calling .ToString() on the src property seems to make mapster not look to much underneath the hood and it works.

Edit: Changed to reflect Luke Briggs suggestion to use a method to return Id instead of ToString(), which indeed works and most likely is faster and safer.

ruffen
  • 1,695
  • 2
  • 25
  • 51
  • 1
    Probably a better version is to add `public int GetId() { return Id; }` to `B` and use `src => src.GetId()` instead. Just based on what I see in the stack trace above (I've not used Mapster) it tries to generate a faster version of the mapping function by analysing the expression you give it but fails because the expression is ambiguous if viewed purely textually, indicating a bug in the library. Because it tries such codegen probably performance is the reason the lib is being used at all, so keep in mind that string + parse is going to absolutely ruin that performance :) – Luke Briggs Oct 28 '22 at 18:34
  • That would indeed be a faster and safer workaround. – ruffen Oct 28 '22 at 18:36