Mapster маппинг на поле с объектом с несколькими конструкторами без дефолтного
Разбираюсь с Mappster - есть такие классы:
class In
{
public string A;
public string B;
public string C;
}
class Out
{
public Sub Sub;
public string C;
}
class Sub
{
public string A;
public string B;
public Sub(string a, string b)
=>(A,B) = (a,b);
}
Задаю правила:
var mapper = new Mapper();
mapper.Config
.ForType<In, Out>()
.Map(@out=> @out.Sub, @in => new Sub(@in.A, @in.B));
Если замапить сейчас все нормально, но если добавить в класс Sub еще один конструктор (допустим public Sub(string a)=> A=a;) Маппинг отвалится с ошибкой
Error while compiling
source=UserQuery+In
destination=UserQuery+Out
type=Map
Error while compiling
source=UserQuery+Sub
destination=UserQuery+Sub
type=Map
No default constructor for type 'Sub', please use 'ConstructUsing' or 'MapWith'
Почитал документацию, попробовал так сяк и 'ConstructUsing' и 'MapWith' - не подошли (или, что вероятнее, я что-то не так делаю).
Ответы (2 шт):
using MapsterMapper;
static class MapperExtensions
{
public static Out Map(this IMapper mapper, In @in)
{
return new Out
{
Sub = new Sub(@in.A, @in.B),
C = @in.C
};
}
}
Проверяем:
In i = new In { A = "a", B = "b", C = "c" };
IMapper mapper = new Mapper();
Out o = mapper.Map(i);
Console.WriteLine(o.Sub.A + " " + o.Sub.B + " " + o.C);
Я всегда считал и продолжаю считать, что любая утилита/библиотека должна упрощать написание кода. Автоматические мапперы можно применять, но только до тех пор, пока они делают свою работу автоматически. Как только приходится описывать маппинг вручную в конфиге маппера, то лучше от него отказаться.
В показанном мной примере ручной маппинг элементарен. В то время как конфиг с описанием правил для маппера в несколько раз больше. Так зачем писать больше?
The reasons behind why I don't use AutoMapper - замечательная статья (и вообще блог хороший), рекомендую к прочтению. В комментариях ссылка на ответ автора этого АвтоМаппера, где он юлит, но в итоге признаёт, что чаще всего нет смысла использовать его детище.
Нашел, достаточно в конфиге добавить такое
config.ForType<Sub, Sub>().MapToConstructor(true);
Тогда сработает даже мой специфический кейс:
void Main()
{
var mapper = new Mapper();
var config = mapper.Config;
config
.ForType<In, Out>()
.Map(@out => @out.Sub, @in => new Sub(@in.A, @in.B))
.Map(@out => @out.Sub2, @in => new Sub(@in.A2, @in.B2));
config.ForType<Sub, Sub>().MapToConstructor(true);
var @in = new In
{
A = "A1",
A2 = "A2",
B = "B1",
B2 = "B2",
C ="Cfff"
};
@in.Dump();
var @out = mapper.Map<Out>(@in);
@out.Dump();
}
class In
{
public string A;
public string B;
public string A2;
public string B2;
public string C;
}
class Out
{
public Sub Sub;
public Sub Sub2;
public string C;
}
class Sub
{
public string A;
public string B;
public Sub(string a, string b)
=> (A, B) = (a, b);
public Sub(string a)
=> (A, B) = (a, null);
}