Home > OS >  Automapper and Nullable Reference Type raise "needs to have a constructor with 0 args or only o
Automapper and Nullable Reference Type raise "needs to have a constructor with 0 args or only o

Time:03-04

Scenario

I can't map a class with string and nullable reference type enabled

#r "nuget:AutoFixture/4.17.0"
#r "nuget:AutoMapper/11.0.1"

using AutoFixture;
using AutoMapper;

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

    public string Title { get; set; }

    public string Content { get; set; }

    public IEnumerable<Tag>? Tags { get; set; }

    public Song()
    {
        Title = string.Empty;
        Content = string.Empty;
    }
}

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

    public string Title { get; set; }

    public string Color { get; set; }

    public Tag()
    {
        Title = string.Empty;
        Color = string.Empty;
    }
}


class DetailSong
{
    public string Title { get; set; }

    public string Content { get; set; }

    public IEnumerable<int>? Tags { get; set; }

    public DetailSong(string title, string content, IEnumerable<int>? tags)
    {
        Title = title;
        Content = content;
        Tags = tags;
    }

}

class CreateSong : DetailSong
{
    public string Author { get; set; }

    public CreateSong(string author, string title, string content, IEnumerable<int>? tags) :
        base(title, content, tags)
    {
        Author = author;
    }
}

var config = new MapperConfiguration(cfg =>
{

    cfg.CreateMap<Tag, int>()
                .ConstructUsing(p => p.Id)
                ;

    cfg.CreateMap<Song, CreateSong>()
        .ForMember(p => p.Author, o => o.MapFrom(k => "dummy"))
        .ForMember(p => p.Content, o => o.MapFrom(k => k.Content))
        .ForMember(p => p.Title, o => o.MapFrom(k => k.Title))
        ;

});

var mapper = config.CreateMapper();

Fixture fixture = new Fixture();
var s = fixture.Create<Song>();

// throw needs to have a constructor with 0 args or only optional args. Validate your configuration for details. (Parameter 'type')
var m = mapper.Map<CreateSong>(s);

Workaround

I found two workaround:

One

If I specify .ForCtorParam("author", o => o.MapFrom(k => "dummy")) on CreateMap it work correctly

cfg.CreateMap<Song, CreateSong>()
        .ForCtorParam("author", o => o.MapFrom(k => "dummy")) // <--------
        .ForMember(p => p.Content, o => o.MapFrom(k => k.Content))
        .ForMember(p => p.Title, o => o.MapFrom(k => k.Title))
        
        ;

Two

Add a args 0 ctor

public CreateSong() : base(string.Empty, string.Empty, null)
{
        Author = string.Empty;
}

Question

Why Automapper can map title, content and tags but not the author field? I suspect is it linked to nullable reference type.

CodePudding user response:

It is not related to the nullable reference types. Automapper is capable of mapping fields/properties to constructor parameters based on names and there is nothing to match author parameter so the original mapping fails. I.e. next will work:

cfg.CreateMap<Song, CreateSong>()
    .ForCtorParam("author", o => o.MapFrom(k => "dummy"));

Also note that ForMember calls are actually ignored. Try changing mapping to:

cfg.CreateMap<Song, CreateSong>()
    .ForCtorParam("author", o => o.MapFrom(k => "dummy"))
    .ForMember(p => p.Content, o => o.MapFrom(k => k.Content   "_Test"))
    .ForMember(p => p.Title, o => o.MapFrom(k => k.Title   "_Test")) ;

And you will see that it does not affect result.

  • Related