coolboy
coolboy

Reputation: 97

Mapster: Mapping Properties of different types, No default constructor for type 'CultureInfo', please use 'ConstructUsing' or 'MapWith'

I use Mapster with version 7.3.0 and .Net 7.0.1

I have two classes.

public class Entity
{
    public Guid Id { get; set; }

    public string Content { get; set; } = default!;

    public string Note { get; set; } = default!;

    public CultureInfo CultureInfo { get; set; } = default!;
}

public class Dto
{
    [Required, StringLength(64)]
    public string Id { get; set; } = default!;

    [Required, StringLength(4096)]
    public string Content { get; set; } = default!;

    [Required, StringLength(1024)]
    public string Note { get; set; } = default!;

    [Required]
    public string CultureName { get; set; } = default!;
}

And with mapster config

TypeAdapterConfig<Dto, Entity>.NewConfig()
            .Map(entity => entity.CultureInfo, dto => CultureInfo.GetCultureInfo(dto.CultureName));
TypeAdapterConfig<Entity, Dto>.NewConfig()
            .Map(dto => dto.CultureName, entity => entity.CultureInfo.Name);

Now execute this code

//Mapster configuration is applied.

var dto = new Dto
        {
            Id = Guid.NewGuid().ToString(),
            Content = "this is a test content",
            Note = "for test",
            CultureName = "zh-CN"
        };
        var mLocal = dto.Adapt<Entity>();

var mLocal = dto.Adapt<Entity>(); throws a Mapster.CompileException: Error while compiling.

Mapster.CompileException: Error while compiling

Mapster.CompileException
Error while compiling
source=MindOcean.Models.Common.MessageTemplateLocalizerDto
destination=MindOcean.Data.Entities.MessageTemplateLocalizer
type=Map
   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 MindOcean.Test.UnitTest.MapsterTester.TestTwoWay() in /Test/MapsterTester.cs:line 71
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)

Mapster.CompileException
Error while compiling
source=System.Globalization.CultureInfo
destination=System.Globalization.CultureInfo
type=Map
   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.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)

System.InvalidOperationException
No default constructor for type 'CultureInfo', please use 'ConstructUsing' or 'MapWith'
   at Mapster.Adapters.BaseAdapter.CreateInstantiationExpression(Expression source, Expression destination, CompileArgument arg)
   at Mapster.Adapters.ClassAdapter.CreateInstantiationExpression(Expression source, Expression destination, CompileArgument arg)
   at Mapster.Adapters.BaseAdapter.CreateInstantiationExpression(Expression source, CompileArgument arg)
   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)

No default constructor for type 'CultureInfo', please use 'ConstructUsing' or 'MapWith'

I tried use MapWith and it worked.

TypeAdapterConfig<Dto, Entity>.NewConfig()
     .MapWith(dto => new Entity
     {
         CultureInfo = CultureInfo.GetCultureInfo(dto.CultureName),
         Id = Guid.Parse(dto.Id),
         Content = dto.Content,
         Note = dto.Note,
     });
TypeAdapterConfig<Entity, Dto>.NewConfig()
            .Map(dto => dto.CultureName, entity => entity.CultureInfo.Name);

I prefer TypeAdapterConfig<Dto, Entity>.NewConfig() this way. If there are a lot of properties in the Entity class, and I only need to configure the CultureInfo property, the configuration of the other properties is unnecessary, so I don't have to write so much unnecessary code.MapWith is like I'm converting manually and with a lot of unnecessary code.

I want to know is there an another way rather than MapWith to solve this mapster mapping configuration problem?

Upvotes: 4

Views: 5198

Answers (1)

Guru Stron
Guru Stron

Reputation: 143511

Not sure why it does not work, but at least one workaround can be ignoring the member and using after mapping action:

TypeAdapterConfig<Dto, Entity>.NewConfig()
    .Ignore(e => e.CultureInfo)
    .AfterMapping((d, e) => e.CultureInfo = CultureInfo.GetCultureInfo(d.CultureName));

Upvotes: 6

Related Questions