Przejdź do treści

Facet w .NET: Koniec z ręcznym pisaniem DTO i wolnym mapowaniem

  • przez

W świecie programowania w .NET, walka z tzw. „boilerplate code” (kodem powtarzalnym) to chleb powszedni. Każdy programista backendowy zna ten ból: masz encję domenową, ale potrzebujesz ją zwrócić z API. Tworzysz więc klasę DTO. Następnie piszesz mapper (lub konfigurujesz AutoMapper). Potem, przy zapytaniu do bazy danych, musisz pamiętać o projekcji (.Select()), aby nie pobierać całej bazy do pamięci.

Tutaj na scenę wkracza Facet – biblioteka, która wykorzystuje jedną z najpotężniejszych funkcji współczesnego C#: Source Generators.

Czym jest Facet?

Facet to biblioteka NuGet, która automatycznie generuje obiekty DTO (Data Transfer Objects), kod mapujący oraz projekcje EF Core w czasie kompilacji.

W przeciwieństwie do tradycyjnych bibliotek mapujących (jak AutoMapper), które często działają w oparciu o Refleksję (Reflection) w czasie działania aplikacji (runtime), Facet tworzy gotowy kod C# w momencie, gdy budujesz projekt.

Główne zalety:

  1. Zero narzutu w Runtime: Ponieważ kod jest generowany podczas kompilacji, działa tak szybko, jak ręcznie napisany kod. Nie ma kosztownej refleksji.
  2. Bezpieczeństwo typów (Type Safety): Jeśli zmienisz nazwę właściwości w encji, kompilator natychmiast poinformuje Cię o błędzie w DTO, zanim w ogóle uruchomisz aplikację.
  3. Projekcje SQL: Biblioteka potrafi wygenerować wydajne zapytania LINQ (Select), pobierając z bazy danych tylko te kolumny, które są faktycznie potrzebne w Twoim DTO.

Jak to działa w praktyce?

Załóżmy, że masz prostą encję w systemie e-commerce:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string InternalSkuCode { get; set; } // Tego nie chcemy zwracać klientowi
    public Category Category { get; set; }
}

Zamiast ręcznie tworzyć ProductDto i pisać kod kopiujący dane, używasz atrybutu [Facet].

Krok 1: Definicja

Definiujesz klasę DTO jako partial i oznaczasz ją atrybutem wskazującym na encję źródłową:

using Facet;

[Facet(typeof(Product))]
public partial class ProductDto 
{
    // Możesz wskazać, które pola chcesz, lub wykluczyć niechciane.
    // Domyślnie Facet spróbuje dopasować właściwości po nazwach.
}

Krok 2: Magia kompilacji

W tle, Facet wygeneruje drugą część klasy ProductDto zawierającą właściwości Id, Name, Price (pominie te, których nie ma w DTO lub są wykluczone) oraz metody mapujące.

Krok 3: Użycie (np. z Entity Framework Core)

To tutaj Facet błyszczy najjaśniej. Zamiast pobierać całą encję, używasz wygenerowanej metody rozszerzającej:

var products = await dbContext.Products
    .Where(p => p.Price > 100)
    .ToProductDtoListAsync(); // Wygenerowana metoda!

Ten kod zostanie przetłumaczony na optymalny SQL, pobierający tylko wybrane kolumny.

Zaawansowane funkcje: Flattening

Jedną z nowszych i bardzo przydatnych funkcji jest spłaszczanie (Flattening). Często zdarza się, że encja ma zagnieżdżone obiekty (np. Product.Category.Name), a my chcemy zwrócić płaską strukturę JSON.

Tradycyjnie musiałbyś pisać: CategoryName = p.Category.Name

Z Facet, jeśli nazwiesz właściwość w DTO zgodnie z konwencją (np. CategoryName), biblioteka często domyśli się, o co chodzi, lub możesz to skonfigurować atrybutem, a generator utworzy odpowiedni kod nawigujący po relacjach.

Facet vs AutoMapper vs Mapster

CechaFacetAutoMapperRęczne pisanie kodu
Moment generowaniaKompilacja (Source Gen)Runtime (zazwyczaj)Czas pisania kodu
WydajnośćBardzo wysokaŚrednia (narzut inicjalizacji)Bardzo wysoka
Wykrywanie błędówPodczas kompilacji (Red squiggle)Często dopiero po uruchomieniuPodczas kompilacji
Ilość kodu do napisaniaMała (Atrybuty)Mała (Profile)Duża
DebugowanieŁatwe (masz wgląd w wygenerowany kod)Trudne (magia w tle)Łatwe

Podsumowanie

Biblioteka Facet to świetny przykład tego, w jakim kierunku zmierza ekosystem .NET. Odchodzimy od „magicznych”, wolnych rozwiązań opartych na refleksji na rzecz rozwiązań generowanych statycznie (AOT-friendly).

Jeśli budujesz nowe API w .NET 6/7/8+ i chcesz zachować czystość architektury bez poświęcania wydajności i bez pisania setek identycznych klas DTO – Facet jest biblioteką, którą zdecydowanie warto sprawdzić.

Tagi: