diff --git a/src/After/DbContext.cs b/src/After/DbContext.cs
index 3651c04..ff0e53b 100644
--- a/src/After/DbContext.cs
+++ b/src/After/DbContext.cs
@@ -115,6 +115,46 @@ public class DbContext
_byRef[order] = fresh;
}
+ // ── loading: reassemble domain objects from the stored rows ──────
+
+ ///
+ /// Materialize the stored under ,
+ /// including all of its orders (navigation fix-up wires
+ /// back). Null when nothing is stored under it.
+ ///
+ public Client? LoadClient(Guid id)
+ {
+ var row = Find(r => r.Id == id);
+ if (row is null) return null;
+ var client = Mappers.FromClientData(row);
+ foreach (var orderRow in _orders.Where(o => o.ClientId == id))
+ client.Orders.Add(Mappers.FromOrderData(orderRow)); // fix-up wires Order.Client
+ return client;
+ }
+
+ ///
+ /// Materialize the stored under ,
+ /// including its client (holding just this one order), or null when
+ /// nothing is stored under it. Use to load a
+ /// client with all of its orders.
+ ///
+ public Order? LoadOrder(Guid id)
+ {
+ var row = Find(r => r.Id == id);
+ if (row is null) return null;
+ var order = Mappers.FromOrderData(row);
+ if (row.ClientId != Guid.Empty)
+ {
+ var clientRow = Find(c => c.Id == row.ClientId);
+ if (clientRow is not null)
+ {
+ var client = Mappers.FromClientData(clientRow);
+ client.Orders.Add(order); // fix-up wires Order.Client
+ }
+ }
+ return order;
+ }
+
// ── query helpers ───────────────────────────────────────────────
/// Search the per-type table for a record whose predicate matches.
diff --git a/src/After/Mappers.cs b/src/After/Mappers.cs
index 520ec01..c9f016b 100644
--- a/src/After/Mappers.cs
+++ b/src/After/Mappers.cs
@@ -138,4 +138,29 @@ public static class Mappers
ClientId = order.Client?.Id ?? Guid.Empty,
};
}
+
+ // ------------------------------------------------------------------
+ // Persistence -> domain (read-back). A plain field copy for now; when
+ // the domain grows OrderLines, FromOrderData becomes the one-line
+ // reconstruction (row description + total become a single line) —
+ // see EXERCISE2.md.
+ // ------------------------------------------------------------------
+
+ /// Map a record back to a .
+ public static Client FromClientData(ClientData row)
+ {
+ ArgumentNullException.ThrowIfNull(row);
+ return new Client { Id = row.Id, Name = row.Name };
+ }
+
+ ///
+ /// Map an record back to an .
+ /// Reassembling the graph (wiring ) is the
+ /// context's job, not the mapper's.
+ ///
+ public static Order FromOrderData(OrderData row)
+ {
+ ArgumentNullException.ThrowIfNull(row);
+ return new Order { Id = row.Id, Description = row.Description, TotalInCents = row.TotalInCents };
+ }
}
\ No newline at end of file
diff --git a/tests/BeforeAfter.Tests/AfterPersistenceTests.cs b/tests/BeforeAfter.Tests/AfterPersistenceTests.cs
index 08d1478..eb859a3 100644
--- a/tests/BeforeAfter.Tests/AfterPersistenceTests.cs
+++ b/tests/BeforeAfter.Tests/AfterPersistenceTests.cs
@@ -88,4 +88,61 @@ public class AfterPersistenceTests
Assert.Equal(order.Id, found!.Id);
Assert.Null(db.Find(row => row.Description == "no such order"));
}
+
+ // ---------------------------------------------------------------------
+ // Read-back: LoadOrder / LoadClient reassemble domain objects from the
+ // stored rows (identity round-trip for now — OrderLines come later).
+ // ---------------------------------------------------------------------
+
+ [Fact]
+ public void Loading_an_order_restores_its_state_and_its_client()
+ {
+ var db = new DbContext();
+ var client = new Client { Name = "Acme" };
+ var order = new Order { Description = "one", TotalInCents = 150_00 };
+ client.Orders.Add(order);
+ db.Save(client);
+ db.Save(order);
+
+ var loaded = db.LoadOrder(order.Id);
+
+ Assert.NotNull(loaded);
+ Assert.Equal(order.Id, loaded!.Id);
+ Assert.Equal("one", loaded.Description);
+ Assert.Equal(150_00, loaded.TotalInCents);
+
+ // The graph is reassembled: the loaded order knows its client again,
+ // and the client's collection holds the loaded order.
+ Assert.NotNull(loaded.Client);
+ Assert.Equal("Acme", loaded.Client!.Name);
+ Assert.Same(loaded, Assert.Single(loaded.Client.Orders));
+ }
+
+ [Fact]
+ public void Loading_a_client_restores_it_with_all_of_its_orders()
+ {
+ var db = new DbContext();
+ var client = new Client { Name = "Acme" };
+ client.Orders.Add(new Order { Description = "one", TotalInCents = 150_00 });
+ client.Orders.Add(new Order { Description = "two", TotalInCents = 250_00 });
+ db.Save(client);
+ db.Save(client.Orders[0]);
+ db.Save(client.Orders[1]);
+
+ var loaded = db.LoadClient(client.Id);
+
+ Assert.NotNull(loaded);
+ Assert.Equal("Acme", loaded!.Name);
+ Assert.Equal(2, loaded.Orders.Count);
+ Assert.All(loaded.Orders, o => Assert.Same(loaded, o.Client));
+ }
+
+ [Fact]
+ public void Loading_an_unknown_id_returns_null()
+ {
+ var db = new DbContext();
+
+ Assert.Null(db.LoadOrder(Guid.NewGuid()));
+ Assert.Null(db.LoadClient(Guid.NewGuid()));
+ }
}