Nel precedente script abbiamo visto come l'utilizzo di una entità di Entity Framework possa portare a problemi nella serializzazione JSON a causa delle referenze circolari. L'esempio citato, però, è solo la punta dell'iceberg di una serie di problematiche che sorgono nel momento in cui esponiamo direttamente a utilizzatori esterni le entità di dominio.
Il modo corretto di procedere in queste casistiche, infatti, è quello di esporre il dato sotto forma di DTO (Data Transfer Object), ossia oggetti creati appositamente per essere condivisi verso l'esterno, magari con una strutturazione differente rispetto alle entità. I benefici sono molteplici:
- Se il dominio dovesse variare, i DTO assicurano che il messaggio scambiato con l'esterno rimanga invece il medesimo;
- tipicamente i modelli di dominio hanno una sovrastrutturazione rispetto a quanto è necessario comunicare all'esterno: per esempio, per un ordine può essere sufficiente una proprietà stringa con il nome del cliente, piuttosto che una reference verso una entità cliente;
- le entità di dominio possono avere dei dati che non vogliamo esporre all'esterno, quali identificativi, campi di servizio, informazioni riservate.
Consideriamo l'esempio di un ordine:
public class Order { public int Id { get; set; } public DateTime Date { get; set; } public Customer Customer { get; set; } ... }
Un data transfer object corrispondente potrebbe avere la seguente forma:
public class OrderDto { public DateTime Date { get; set; } public string Customer { get; set; } ... }
Gestire oggetti di questo tipo, però, può risultare molto tedioso, perché ci troviamo spesso a fare assegnazioni di proprietà tra l'una e l'altra tipologia di ordine. Proprio per evitare tutto questo codice boiler plate, possiamo utilizzare un tool, denominato Automapper, che è disponibile gratuitamente su NuGet.
Grazie ad Automapper, infatti, possiamo creare un mapping tra Order e OrderDto in modo che le assegnazioni vengano effettuate in maniera del tutto automatica. Allo startup dell'applicazione, non dobbiamo far altro che scrivere questo codice:
protected void Application_Start() { // ... Mapper.CreateMap<Order, OrderDto>() .ForMember(x => x.Customer, x => x.MapFrom(y => y.Customer.Name)); Mapper.AssertConfigurationIsValid(); }
La logica di funzionamento che regola la conversione è molto semplice: dove non specificato diversamente, tutte le proprietà dell'oggetto di destinazione (in questo caso OrderDto) vengono recuperate dalle proprietà omonime dell'oggetto origine (e quindi Order). Ovviamente questa regola non è sempre applicabile, e per questa ragione Automapper consente di specificare una serie di direttive. Per esempio, nel codice in alto, stiamo mappando la proprietà OrderDto.Customer, di tipo stringa, con la proprietà Name di Order.Customer.
La chiamata ad AssertConfigurationIsValid va poi effettuata a valle della configurazione, per verificare che tutte le impostazioni siano corrette.
Ciò che è importante, è che con i DTO abbiamo assoluto controllo di ciò che, per esempio, esponiamo all'esterno nella forma di un oggetto serializzato in formato JSON:
public ActionResult JsonDemo(int id) { var order = OrderRepository.Get(id); var model = Mapper.Map<OrderDto>(order); return this.Json(order, JsonRequestBehavior.AllowGet); }
Automapper è molto flessibile e presenta diverse opzioni di personalizzazione, per le quali rimandiamo alla guida ufficiale:
https://github.com/AutoMapper/AutoMapper/wiki
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Filtrare i dati di una QuickGrid in Blazor con una drop down list
Disabilitare automaticamente un workflow di GitHub
Registrare servizi multipli tramite chiavi in ASP.NET Core 8
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
Usare le navigation property in QuickGrid di Blazor
Persistere la ChatHistory di Semantic Kernel in ASP.NET Core Web API per GPT
.NET Conference Italia 2024
Ottimizzare il mapping di liste di tipi semplici con Entity Framework Core
Eseguire query per recuperare il padre di un record che sfrutta il tipo HierarchyID in Entity Framework
Effettuare il refresh dei dati di una QuickGrid di Blazor
Utilizzare Tailwind CSS all'interno di React: primi componenti
Utilizzare i primary constructor di C# per inizializzare le proprietà