Skip to content

Mapping Graphs (DTO to Entity)

Leonardo Porro edited this page Feb 1, 2023 · 23 revisions

This library was coded to be extensible to other ORMs, and it can map graphs of DTOs to entities and DTOs to other DTOs, but the main and most useful feature is to map objects to Microsoft EntityFramework entities and set the correct entity state.

The mapper copies data from a source of a given type to a target that might be of the same type or not. For the EntityFramework extension, a source DTO graph is passed as a parameter to the dbContext.Map<TEntity> or dbContext.MapAsync<TEntity> method, then the target entity graph is loaded from the database, using a query by key. The key values are readed from the source parameter.

Diagram

After loading the target graph, it is traversed (depth-first) and members are recursively mapped using the current configuration.

Primitive values are copied as-is or with some type conversion (e.g.: string to int). If there is any difference between source and target values, the entity state is set to Modified, otherwise, is set to Unchanged.

In the case of aggregations only the key members are mapped, and the rest of values are ignored, the entity state is set to Unchanged, because no changes should be done outside of the current scope, and also independent entities are supposed to exist before the current map is executed.

For compositions a merge by key is performed.

  • If the DTO exists in source but the entity doesn't exist in target, the entity is created and the entity state is set to Added.
  • If the DTO and entity exist in both, source and target, its members are mapped, and the entity state is set to Modified.
  • Finally if the entity exists in target only, its state is set to Deleted.

The same logic is applied to aggregations and compositions for reference and collection navigation properties.

The result of the call to dbContext.Map<TEntity> is the root entity of the entity graph that has been loaded/created, and each entity on it has the right entity state set. To persist the changes made by the mapper, a regular call to dbContext.SaveChanges must be done, in the same way as if the mapping were done manually. The user may apply custom changes to the entity graph before doing the call.

For clarity, this example uses Entity name + DTO for the source objects, that's probably not the best approach, there are some suggestions on Entities as DTOs.