[Feature Request] Expand capability for factory methods
So I'm seeing a limitation with the current capabilities when using a factory method.
The Problem
Background
Say I have a (simplified) entity that looks like this:
public class Author : BaseEntity
{
public virtual string Name { get; private set; }
public static Author Create(AuthorForCreationDto authorForCreationDto)
{
var config = new TypeAdapterConfig();
config.Apply(new AuthorProfile());
var mapper = new MapsterMapper.Mapper(config);
return mapper.Map<Author>(authorForCreationDto);
}
public void Update(AuthorForUpdateDto authorForUpdateDto)
{
var config = new TypeAdapterConfig();
config.Apply(new AuthorProfile());
var mapper = new MapsterMapper.Mapper(config);
mapper.Map(authorForUpdateDto, this);
}
public class AuthorForCreationDto
{
public string Name { get; set; }
}
public class AuthorForUpdateDto
{
public string Name { get; set; }
}
protected Author() { } // For EF + Mocking
}
Given this setup, when I set up my mapper and try to create an Author, I can't because I don't have a default constructor.
public class AuthorProfile : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<Author, AuthorDto>()
.TwoWays();
config.NewConfig<AuthorForCreationDto, Author>()
.TwoWays();
config.NewConfig<Author, AuthorForUpdateDto>()
.TwoWays();
}
}
Error:
System.InvalidOperationException : No default constructor for type 'Author', please use 'ConstructUsing' or 'MapWith'
The Gap In Mapster
When I try and use 'ConstructUsing' or 'MapWith' like so:
config.NewConfig<AuthorForCreationDto, Author>()
.ConstructUsing(x => Author.Create(x));
I get an error, presumably because it is recursively trying to use a mapping inside the creation factory and using the factory for the mapping.
Exit code is 134 (Output is too long. Showing the last 100 lines:
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[[System.Int32, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
This makes sense given the current setup, but seems to literally block me from leveraging my mappings in my factories. And while the Create factory has a theoretical path, the Update factory is totally blocked as i have no way to construct it.
Discussion
Something like this is doable in Automapper with something like the below (using reflection I think?).
I don't have an immediate idea or recommendation for solving this, but I wanted to present the problem to see if you had any ideas.
Working Automapper
public class Recipe : BaseEntity
{
public virtual string Title { get; private set; }
public static Recipe Create(RecipeForCreationDto recipeForCreationDto)
{
var mapper = new Mapper(new MapperConfiguration(cfg => {
cfg.AddProfile<RecipeProfile>();
}));
return mapper.Map<Recipe>(recipeForCreationDto);
}
public void Update(RecipeForUpdateDto recipeForUpdateDto)
{
var mapper = new Mapper(new MapperConfiguration(cfg => {
cfg.AddProfile<RecipeProfile>();
}));
mapper.Map(recipeForUpdateDto, this);
}
protected Recipe() { } // For EF + Mocking
}
public class RecipeProfile : Profile
{
public RecipeProfile()
{
CreateMap<Recipe, RecipeDto>()
.ReverseMap();
CreateMap<Recipe, RecipeForCreationDto>()
.ReverseMap();
CreateMap<Recipe, RecipeForUpdateDto>()
.ReverseMap();
}
}