MapsterMapper / Mapster

A fast, fun and stimulating object to object Mapper
MIT License
4.31k stars 328 forks source link

Can't map to Nullable<T> property #389

Open cytoph opened 2 years ago

cytoph commented 2 years ago

The following code just won't work:

public class MyClass
{
    public string Code { get; set; }
    public string Name { get; set; }
    public MyStruct? StructValue { get; set; }
}

public struct MyStruct
{
    public Enum1 Value1 { get; set; }
    public Enum2 Value2 { get; set; }
}

public static TypeAdapterConfig GetConfig()
{
    var config = new TypeAdapterConfig();

    config.NewConfig<DataRow, MyClass>()
        .Map(c => c.Code, dr => dr.Field<string>("Code"))
        .Map(c => c.Name, dr => dr.Field<string>("Name"))
        .Map(c => c.StructValue, dr => dr, dr => ShouldMapStructValue(dr))
        .IgnoreNonMapped(true);

    config.NewConfig<DataRow, MyStruct>()
        .Map(c => c.Value1, dr => EnumHelper.ParseEnum1(dr.Field<string>("Enum1")))
        .Map(c => c.Value2, dr => EnumHelper.ParseEnum2(dr.Field<string>("Enum2")))
        .IgnoreNonMapped(true);
}

public static void Main(params string[] args)
{
    var container = new DryIoc.Container();

    container.RegisterInstance(GetConfig());
    container.Register<IMapper, ServiceMapper>(Reuse.Scoped);

    var mapper = container.Resolve<IMapper>();

    DataRow dataRow = GetDataRowFromDatabase();

    var myClass = mapper.Map<MyClass>(dataRow);
}

The last line (the one where the mapping itself is done) throws the following exception:

 ---> Mapster.CompileException: Error while compiling
source=System.Data.DataRow
destination=System.Nullable`1[MyNameSpace.MyStruct]
type=Map
 ---> System.InvalidOperationException: Cannot convert immutable type, please consider using 'MapWith' method to create mapping
   at Mapster.Adapters.PrimitiveAdapter.ConvertType(Expression source, Type destinationType, CompileArgument arg)
   at Mapster.Adapters.PrimitiveAdapter.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.CreateInlineMapExpression(Type sourceType, Type destinationType, MapType mapType, CompileContext context, MemberMapping mapping)
   at Mapster.Adapters.BaseAdapter.CreateAdaptExpressionCore(Expression source, Type destinationType, CompileArgument arg, MemberMapping mapping, Expression destination)
   at Mapster.Adapters.BaseAdapter.CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, MemberMapping mapping, Expression destination)
   at Mapster.Adapters.ClassAdapter.CreateBlockExpression(Expression source, Expression destination, CompileArgument arg)
   at Mapster.Adapters.BaseAdapter.CreateBlockExpressionBody(Expression source, Expression destination, 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 MapsterMapper.Mapper.Map[TDestination](Object source)
   at MapsterMapper.ServiceMapper.Map[TDestination](Object source)
   [...] --- removed for privacy reasons
BabaDorin commented 2 years ago

same here

OFark commented 2 years ago

You have to create a mapping for the nullable types as well. NewConfig<DataRow?, MyStruct?> I'm guessing assumptions can't be made that null maps to null, or maybe unwrapping is too expensive, but whatever the case, if you have a nullable you need to specify that as another Map.