Webes alkalmazások esetén nem célszerű közvetlenül adatátviteli objektumnak használni olyan objektumokat, amelyek logikával is rendelkeznek és/vagy az adatbázis modell részét képezik.
Ennek oka az, hogy ezek az osztályok nem biztos, hogy egy olyan rétegben helyezkednek el az architektúránkban, hogy könnyen közzé lehessen Å‘ket tenni. De ugyan ilyen nyomós érv lehet a flexibilitás is. Ha leválasztjuk az adatátviteli objektumainkat (DTO – Data Transfer Object) a tényleges adatbázis modellünktÅ‘l, akkor az egyik módosÃtása nem vonzza magával a másik módosÃtását.
Ebben az esetben azonban egy olyan problémával találjuk magunkat szemben, hogy adatot kell konvertálnunk valahogy két hasonló felépÃtésű objektum között. Erre ad megoldást a mapper.
Nézzünk egy mapper implementációt:
internal class Student
{
public string Name { get; init; }
public int Class { get; init; }
public double Average { get; init; }
}
public class StudentDto
{
public string Name { get; init; }
public int Class { get; init; }
public double Average { get; init; }
}
internal static class Mapper
{
public static StudentDto Map(this Student student)
{
return new StudentDto
{
Name = student.Name,
Class = student.Class,
Average = student.Average
};
}
}
A mapper többféleképpen implementálható. C# esetén célszerű extension method segÃtségével implementálni. Jelen esetben ezt a Map metódus valósÃtja meg, ami egy Student objektumot StudentDto objektummá konvertál a tulajdonságainak a másolásával. Opcionálisan a mapper végezhet transzformációs logikát is, de alapvetÅ‘en nem ez a célja.
Előnyök
- Leválasztás: A kód különálló részei közötti nem lesz kapcsolat
- Rugalmasság: A kód egyik részének a módosÃtása nem hat a másikra
Hátrányok
- Növeli a kód komplexitását
- A konverziós logika bonyolultságától és futásának gyakoriságától csökkenhet a rendszer áteresztőképessége
Univerzális mapper
Mapper kódot kézzel Ãrni nem a legjobb ötlet, mivel repetatÃv feladat és könnyen hiba forrás lehet. Éppen ezért léteznek univerzális mapper megoldások, amelyek segÃtségével a probléma lényegesen egyszerűsÃthetÅ‘. Például a tulajdonságok nevei és tÃpusai alapján reflection segÃtségével könnyen implementálhatunk mi magunk is egy mappert, vagy használhatunk egy kész megoldást erre.
Egy ilyen megoldás az Automapper.
Az automapper reflection segÃtségével valósÃt meg kétirányú konverziót. A használatának elsÅ‘ lépése, hogy konfiguráljuk, hogy mit mibe szeretnénk átalakÃtani:
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Student, StudentDto>();
});
Ezt követően a konfiguráció alapján létrehozzuk a mapper komponenst:
var mapper = configuration.CreateMapper();
Ez után a mapper Map metódusával konvertálni tudunk mind a két irányba, a korábban konfigurált átalakÃtások alapján.
var dto = mapper.Map<StudentDto>(student);
var stduent = mapper.Map<Student>(dto);
Mivel ez a megoldás reflectiont használ, ezért sebességében lassabb lesz, mint a kézzel megÃrt kód. Ez azonban nem jelenti azt, hogy kézzel kellene mapping logikát Ãrnunk. C# esetén használhatunk kód generátorokat és a Roslyn kód generátor funkcionalitását használja a Mapster nevű mapper.
- Automapper: https://github.com/AutoMapper/AutoMapper
- Mapster: https://github.com/MapsterMapper/Mapster