order total: TotalInCents on both Orders, mapped into OrderDto and OrderData
yak: ordertotalincents-on-both-sides-dto-mapping-j00p
This commit is contained in:
@@ -29,6 +29,9 @@ public sealed class OrderData : DbBase
|
|||||||
/// <summary>Free-text description of what this order is for.</summary>
|
/// <summary>Free-text description of what this order is for.</summary>
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>Order total in cents of the stored order.</summary>
|
||||||
|
public int TotalInCents { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Foreign key to the owning client: <see cref="Guid.Empty"/> when the
|
/// Foreign key to the owning client: <see cref="Guid.Empty"/> when the
|
||||||
/// order has no client, or when its client had no id yet at save time —
|
/// order has no client, or when its client had no id yet at save time —
|
||||||
@@ -101,6 +104,7 @@ public class DbContext
|
|||||||
{
|
{
|
||||||
var row = (OrderData)existing; // refresh, keep row identity
|
var row = (OrderData)existing; // refresh, keep row identity
|
||||||
row.Description = fresh.Description;
|
row.Description = fresh.Description;
|
||||||
|
row.TotalInCents = fresh.TotalInCents;
|
||||||
row.ClientId = fresh.ClientId;
|
row.ClientId = fresh.ClientId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public static class Mappers
|
|||||||
{
|
{
|
||||||
Id = order.Id,
|
Id = order.Id,
|
||||||
Description = order.Description,
|
Description = order.Description,
|
||||||
|
TotalInCents = order.TotalInCents,
|
||||||
ClientDto = order.Client is null ? null : MapClient(order.Client, seen),
|
ClientDto = order.Client is null ? null : MapClient(order.Client, seen),
|
||||||
};
|
};
|
||||||
seen[order] = dto;
|
seen[order] = dto;
|
||||||
@@ -102,6 +103,7 @@ public static class Mappers
|
|||||||
{
|
{
|
||||||
Id = dto.Id,
|
Id = dto.Id,
|
||||||
Description = dto.Description,
|
Description = dto.Description,
|
||||||
|
TotalInCents = dto.TotalInCents,
|
||||||
Client = dto.ClientDto is null ? null : UnmapClient(dto.ClientDto, seen),
|
Client = dto.ClientDto is null ? null : UnmapClient(dto.ClientDto, seen),
|
||||||
};
|
};
|
||||||
seen[dto] = order;
|
seen[dto] = order;
|
||||||
@@ -132,6 +134,7 @@ public static class Mappers
|
|||||||
{
|
{
|
||||||
Id = order.Id,
|
Id = order.Id,
|
||||||
Description = order.Description,
|
Description = order.Description,
|
||||||
|
TotalInCents = order.TotalInCents,
|
||||||
ClientId = order.Client?.Id ?? Guid.Empty,
|
ClientId = order.Client?.Id ?? Guid.Empty,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ public class Order
|
|||||||
/// <summary>Free-text description of what this order is for.</summary>
|
/// <summary>Free-text description of what this order is for.</summary>
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Order total in cents (integer — no floating-point money). A normal
|
||||||
|
/// attribute: it is persisted in <see cref="OrderData"/> and mapped
|
||||||
|
/// one-on-one into <see cref="OrderDto.TotalInCents"/>.
|
||||||
|
/// </summary>
|
||||||
|
public int TotalInCents { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The client that owns this order. Set automatically by
|
/// The client that owns this order. Set automatically by
|
||||||
/// <see cref="ClientOrders"/> when the order is added to
|
/// <see cref="ClientOrders"/> when the order is added to
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ public sealed record OrderDto
|
|||||||
/// <summary>Free-text description of what this order is for.</summary>
|
/// <summary>Free-text description of what this order is for.</summary>
|
||||||
public string Description { get; init; } = string.Empty;
|
public string Description { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>Order total in cents of the underlying order.</summary>
|
||||||
|
public int TotalInCents { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Back-reference to the owning client, or <c>null</c> for a bare order
|
/// Back-reference to the owning client, or <c>null</c> for a bare order
|
||||||
/// that has not been added to any client.
|
/// that has not been added to any client.
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ DbBase.Context = ctx;
|
|||||||
var jane = new Client { Name = "Jane Doe" };
|
var jane = new Client { Name = "Jane Doe" };
|
||||||
var bob = new Client { Name = "Bob Smith" };
|
var bob = new Client { Name = "Bob Smith" };
|
||||||
|
|
||||||
jane.Orders.Add(new Order { Description = "Design consultation" });
|
jane.Orders.Add(new Order { Description = "Design consultation", TotalInCents = 120_00 });
|
||||||
jane.Orders.Add(new Order { Description = "Website redesign" });
|
jane.Orders.Add(new Order { Description = "Website redesign", TotalInCents = 750_00 });
|
||||||
|
|
||||||
bob.Orders.Add(new Order { Description = "Monthly retainer" });
|
bob.Orders.Add(new Order { Description = "Monthly retainer", TotalInCents = 85_00 });
|
||||||
|
|
||||||
// Orders need to be attached too so they get IDs:
|
// Orders need to be attached too so they get IDs:
|
||||||
ctx.Attach(jane.Orders[0]);
|
ctx.Attach(jane.Orders[0]);
|
||||||
@@ -38,7 +38,7 @@ foreach (var client in DbBase.Context!.Tracked.OfType<Client>())
|
|||||||
{
|
{
|
||||||
Console.WriteLine($"{client.Name} [{client.Id}]");
|
Console.WriteLine($"{client.Name} [{client.Id}]");
|
||||||
foreach (var order in client.Orders)
|
foreach (var order in client.Orders)
|
||||||
Console.WriteLine($" - order [{order.Id}]: {order.Description}");
|
Console.WriteLine($" - order [{order.Id}]: {order.Description} ({order.TotalInCents} cents)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- find by name -------------------------------------------------------
|
// ----- find by name -------------------------------------------------------
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ public class Order : DbBase
|
|||||||
/// <summary>Free-text description of what this order is for.</summary>
|
/// <summary>Free-text description of what this order is for.</summary>
|
||||||
public string Description { get; set; } = string.Empty;
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Order total in cents (integer — no floating-point money). Part of the
|
||||||
|
/// persisted state, like every other scalar.
|
||||||
|
/// </summary>
|
||||||
|
public int TotalInCents { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The client that owns this order. Set automatically by
|
/// The client that owns this order. Set automatically by
|
||||||
/// <see cref="ClientOrders"/> when the order is added to
|
/// <see cref="ClientOrders"/> when the order is added to
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class AfterPersistenceTests
|
|||||||
{
|
{
|
||||||
var db = new DbContext();
|
var db = new DbContext();
|
||||||
var client = new Client { Name = "Acme" };
|
var client = new Client { Name = "Acme" };
|
||||||
var order = new Order { Description = "one" };
|
var order = new Order { Description = "one", TotalInCents = 15000 };
|
||||||
client.Orders.Add(order);
|
client.Orders.Add(order);
|
||||||
|
|
||||||
db.Save(client);
|
db.Save(client);
|
||||||
@@ -29,6 +29,7 @@ public class AfterPersistenceTests
|
|||||||
var orderRow = Assert.Single(db.Orders);
|
var orderRow = Assert.Single(db.Orders);
|
||||||
Assert.Equal(order.Id, orderRow.Id);
|
Assert.Equal(order.Id, orderRow.Id);
|
||||||
Assert.Equal("one", orderRow.Description);
|
Assert.Equal("one", orderRow.Description);
|
||||||
|
Assert.Equal(15000, orderRow.TotalInCents);
|
||||||
Assert.Equal(client.Id, orderRow.ClientId); // the graph is flattened to a key
|
Assert.Equal(client.Id, orderRow.ClientId); // the graph is flattened to a key
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ public class AfterPersistenceTests
|
|||||||
db.Save(order);
|
db.Save(order);
|
||||||
|
|
||||||
order.Description = "one (edited)";
|
order.Description = "one (edited)";
|
||||||
|
order.TotalInCents = 9900;
|
||||||
client.Name = "Acme GmbH";
|
client.Name = "Acme GmbH";
|
||||||
db.Save(order);
|
db.Save(order);
|
||||||
db.Save(client);
|
db.Save(client);
|
||||||
@@ -62,6 +64,7 @@ public class AfterPersistenceTests
|
|||||||
var orderRow = Assert.Single(db.Orders);
|
var orderRow = Assert.Single(db.Orders);
|
||||||
Assert.Equal(order.Id, orderRow.Id);
|
Assert.Equal(order.Id, orderRow.Id);
|
||||||
Assert.Equal("one (edited)", orderRow.Description);
|
Assert.Equal("one (edited)", orderRow.Description);
|
||||||
|
Assert.Equal(9900, orderRow.TotalInCents);
|
||||||
Assert.Equal(client.Id, orderRow.ClientId);
|
Assert.Equal(client.Id, orderRow.ClientId);
|
||||||
|
|
||||||
var clientRow = Assert.Single(db.Clients);
|
var clientRow = Assert.Single(db.Clients);
|
||||||
|
|||||||
@@ -54,12 +54,13 @@ public class AfterTests
|
|||||||
public void ToOrderDto_maps_all_scalar_fields()
|
public void ToOrderDto_maps_all_scalar_fields()
|
||||||
{
|
{
|
||||||
var id = Guid.NewGuid();
|
var id = Guid.NewGuid();
|
||||||
var order = new Order { Id = id, Description = "one" };
|
var order = new Order { Id = id, Description = "one", TotalInCents = 15000 };
|
||||||
|
|
||||||
var dto = Mappers.ToOrderDto(order);
|
var dto = Mappers.ToOrderDto(order);
|
||||||
|
|
||||||
Assert.Equal(id, dto.Id);
|
Assert.Equal(id, dto.Id);
|
||||||
Assert.Equal("one", dto.Description);
|
Assert.Equal("one", dto.Description);
|
||||||
|
Assert.Equal(15000, dto.TotalInCents);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -78,12 +79,13 @@ public class AfterTests
|
|||||||
public void FromOrderDto_maps_all_scalar_fields()
|
public void FromOrderDto_maps_all_scalar_fields()
|
||||||
{
|
{
|
||||||
var id = Guid.NewGuid();
|
var id = Guid.NewGuid();
|
||||||
var dto = new OrderDto { Id = id, Description = "one" };
|
var dto = new OrderDto { Id = id, Description = "one", TotalInCents = 15000 };
|
||||||
|
|
||||||
var order = Mappers.FromOrderDto(dto);
|
var order = Mappers.FromOrderDto(dto);
|
||||||
|
|
||||||
Assert.Equal(id, order.Id);
|
Assert.Equal(id, order.Id);
|
||||||
Assert.Equal("one", order.Description);
|
Assert.Equal("one", order.Description);
|
||||||
|
Assert.Equal(15000, order.TotalInCents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
@@ -129,7 +131,7 @@ public class AfterTests
|
|||||||
public void Domain_round_trip_preserves_all_state()
|
public void Domain_round_trip_preserves_all_state()
|
||||||
{
|
{
|
||||||
var client = new Client { Id = Guid.NewGuid(), Name = "Acme" };
|
var client = new Client { Id = Guid.NewGuid(), Name = "Acme" };
|
||||||
var order = new Order { Id = Guid.NewGuid(), Description = "one" };
|
var order = new Order { Id = Guid.NewGuid(), Description = "one", TotalInCents = 15000 };
|
||||||
client.Orders.Add(order);
|
client.Orders.Add(order);
|
||||||
|
|
||||||
var roundTrip = Mappers.FromClientDto(Mappers.ToClientDto(client));
|
var roundTrip = Mappers.FromClientDto(Mappers.ToClientDto(client));
|
||||||
@@ -139,6 +141,7 @@ public class AfterTests
|
|||||||
var roundTripOrder = Assert.Single(roundTrip.Orders);
|
var roundTripOrder = Assert.Single(roundTrip.Orders);
|
||||||
Assert.Equal(order.Id, roundTripOrder.Id);
|
Assert.Equal(order.Id, roundTripOrder.Id);
|
||||||
Assert.Equal(order.Description, roundTripOrder.Description);
|
Assert.Equal(order.Description, roundTripOrder.Description);
|
||||||
|
Assert.Equal(order.TotalInCents, roundTripOrder.TotalInCents);
|
||||||
// The back-reference is still consistent on the reconstructed graph:
|
// The back-reference is still consistent on the reconstructed graph:
|
||||||
Assert.Same(roundTrip, roundTripOrder.Client);
|
Assert.Same(roundTrip, roundTripOrder.Client);
|
||||||
}
|
}
|
||||||
@@ -159,6 +162,7 @@ public class AfterTests
|
|||||||
var roundTripOrder = Assert.Single(roundTrip.Orders);
|
var roundTripOrder = Assert.Single(roundTrip.Orders);
|
||||||
Assert.Equal(dtoOrder.Id, roundTripOrder.Id);
|
Assert.Equal(dtoOrder.Id, roundTripOrder.Id);
|
||||||
Assert.Equal(dtoOrder.Description, roundTripOrder.Description);
|
Assert.Equal(dtoOrder.Description, roundTripOrder.Description);
|
||||||
|
Assert.Equal(dtoOrder.TotalInCents, roundTripOrder.TotalInCents);
|
||||||
Assert.Same(roundTrip, roundTripOrder.ClientDto);
|
Assert.Same(roundTrip, roundTripOrder.ClientDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +248,7 @@ public class AfterTests
|
|||||||
{
|
{
|
||||||
var app = new WebApp();
|
var app = new WebApp();
|
||||||
var client = new Client { Id = Guid.NewGuid(), Name = "Acme" };
|
var client = new Client { Id = Guid.NewGuid(), Name = "Acme" };
|
||||||
var order = new Order { Id = Guid.NewGuid(), Description = "order one" };
|
var order = new Order { Id = Guid.NewGuid(), Description = "order one", TotalInCents = 15000 };
|
||||||
client.Orders.Add(order);
|
client.Orders.Add(order);
|
||||||
|
|
||||||
var view = app.ShowClient(Mappers.ToClientDto(client));
|
var view = app.ShowClient(Mappers.ToClientDto(client));
|
||||||
|
|||||||
Reference in New Issue
Block a user