Yak: csharp after situation (Yak 02) — plain domain + DTOs + mappers
After/: plain Client/Order (no DbBase), ClientDto/OrderDto records carrying the relations both ways, hand-written Mappers with a reference-identity cache for the back-reference cycle, DbContext with explicit Save(domain) via shadow entities (flipped dependency), and a WebApp that consumes only DTOs. AfterTests cover the 7 required behaviours; full solution builds with 0 warnings, 26/26 tests pass.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
namespace After;
|
||||
|
||||
/// <summary>
|
||||
/// Hand-written mappers between the plain domain and the DTOs (no AutoMapper —
|
||||
/// the mapping code is part of the illustration). Each public method maps in
|
||||
/// exactly one direction and never mutates its input.
|
||||
///
|
||||
/// The <c>Client.Orders</c> / <c>Order.Client</c> back-reference cycle is
|
||||
/// resolved with a reference-identity cache: the same instance maps to the
|
||||
/// same DTO (and back), so the graph terminates and stays consistent.
|
||||
/// </summary>
|
||||
public static class Mappers
|
||||
{
|
||||
/// <summary>Map a <see cref="Client"/> (with its orders) to a <see cref="ClientDto"/>.</summary>
|
||||
public static ClientDto ToClientDto(Client client)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(client);
|
||||
return MapClient(client, new Dictionary<object, object>());
|
||||
}
|
||||
|
||||
/// <summary>Map an <see cref="Order"/> (with its client back-reference) to an <see cref="OrderDto"/>.</summary>
|
||||
public static OrderDto ToOrderDto(Order order)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(order);
|
||||
return MapOrder(order, new Dictionary<object, object>());
|
||||
}
|
||||
|
||||
/// <summary>Map a <see cref="ClientDto"/> (with its orders) back to a <see cref="Client"/>.</summary>
|
||||
public static Client FromClientDto(ClientDto dto)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dto);
|
||||
return UnmapClient(dto, new Dictionary<object, object>());
|
||||
}
|
||||
|
||||
/// <summary>Map an <see cref="OrderDto"/> (with its client back-reference) back to an <see cref="Order"/>.</summary>
|
||||
public static Order FromOrderDto(OrderDto dto)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dto);
|
||||
return UnmapOrder(dto, new Dictionary<object, object>());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Domain -> DTO
|
||||
// ------------------------------------------------------------------
|
||||
private static ClientDto MapClient(Client client, Dictionary<object, object> seen)
|
||||
{
|
||||
if (seen.TryGetValue(client, out var cached))
|
||||
return (ClientDto)cached;
|
||||
|
||||
var dto = new ClientDto { Id = client.Id, Name = client.Name };
|
||||
seen[client] = dto; // cache first: breaks the cycle
|
||||
dto.Orders = client.Orders
|
||||
.Select(o => MapOrder(o, seen))
|
||||
.ToList(); // fill second, through the cache
|
||||
return dto;
|
||||
}
|
||||
|
||||
private static OrderDto MapOrder(Order order, Dictionary<object, object> seen)
|
||||
{
|
||||
if (seen.TryGetValue(order, out var cached))
|
||||
return (OrderDto)cached;
|
||||
|
||||
var dto = new OrderDto
|
||||
{
|
||||
Id = order.Id,
|
||||
Description = order.Description,
|
||||
ClientDto = order.Client is null ? null : MapClient(order.Client, seen),
|
||||
};
|
||||
seen[order] = dto;
|
||||
return dto;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// DTO -> domain
|
||||
// ------------------------------------------------------------------
|
||||
private static Client UnmapClient(ClientDto dto, Dictionary<object, object> seen)
|
||||
{
|
||||
if (seen.TryGetValue(dto, out var cached))
|
||||
return (Client)cached;
|
||||
|
||||
var client = new Client { Id = dto.Id, Name = dto.Name };
|
||||
seen[dto] = client; // cache first: breaks the cycle
|
||||
foreach (var orderDto in dto.Orders)
|
||||
client.Orders.Add(UnmapOrder(orderDto, seen)); // fix-up wires order.Client
|
||||
return client;
|
||||
}
|
||||
|
||||
private static Order UnmapOrder(OrderDto dto, Dictionary<object, object> seen)
|
||||
{
|
||||
if (seen.TryGetValue(dto, out var cached))
|
||||
return (Order)cached;
|
||||
|
||||
var order = new Order
|
||||
{
|
||||
Id = dto.Id,
|
||||
Description = dto.Description,
|
||||
Client = dto.ClientDto is null ? null : UnmapClient(dto.ClientDto, seen),
|
||||
};
|
||||
seen[dto] = order;
|
||||
return order;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user