namespace After; // --------------------------------------------------------------------------- // The persistence side of the "after" situation. The (fake) framework stores // its own data-only records — / , // both subclasses — never the domain objects themselves. // Domain state reaches storage only through explicit mapping; the context keeps // a reference-identity map so a second of the same domain // object refreshes its row instead of duplicating it. // --------------------------------------------------------------------------- /// /// Persisted shape of a : data only — the orders are /// separate rows, so nothing more is needed here. /// public sealed class ClientData : DbBase { /// Display name of the client. public string Name { get; set; } = string.Empty; } /// /// Persisted shape of an : data only. The object graph is /// flattened for storage — the back-reference to the client becomes a foreign /// key, because storage has ids, not object references. /// public sealed class OrderData : DbBase { /// Free-text description of what this order is for. public string Description { get; set; } = string.Empty; /// Order total in cents of the stored order. public int TotalInCents { get; set; } /// /// Foreign key to the owning client: when the /// order has no client, or when its client had no id yet at save time — /// save clients before their orders. /// public Guid ClientId { get; set; } } /// /// The fake DbContext: per-type tables of data records, mirroring EF /// Core's Set<T>. / /// map the domain object's *current* state into its record — inserting on the /// first save, refreshing in place on later ones. /// public class DbContext { // ── per-type data tables ───────────────────────────────────────── private readonly List _clients = new(); private readonly List _orders = new(); // ── fast lookup: domain object reference → its persisted record ── private readonly Dictionary _byRef = new(); // ── exposed per-type tables (read-only view) ───────────────────── public IReadOnlyList Clients => _clients.AsReadOnly(); public IReadOnlyList Orders => _orders.AsReadOnly(); /// Generic accessor: the strongly-typed "table" for . public IReadOnlyList Set() where T : DbBase => typeof(T) switch { var t when t == typeof(ClientData) => (IReadOnlyList)_clients.AsReadOnly(), var t when t == typeof(OrderData) => (IReadOnlyList)_orders.AsReadOnly(), _ => throw new NotSupportedException($"No per-type table for {typeof(T)}"), }; // ── explicit persistence: map + insert, or refresh ─────────────── /// /// Persist a : the first save maps it to a fresh /// row and writes the generated id back into the /// domain object; a later save refreshes that row's data in place. /// public void Save(Client client) { ArgumentNullException.ThrowIfNull(client); var fresh = Mappers.ToClientData(client); if (_byRef.TryGetValue(client, out var existing)) { ((ClientData)existing).Name = fresh.Name; // refresh, keep row identity return; } var id = Guid.NewGuid(); fresh.Id = client.Id = id; // write-back identity fresh.DbContext = this; _clients.Add(fresh); _byRef[client] = fresh; } /// /// Persist an (same insert-or-refresh rule as /// ; save its client first so the foreign key /// can be filled). /// public void Save(Order order) { ArgumentNullException.ThrowIfNull(order); var fresh = Mappers.ToOrderData(order); if (_byRef.TryGetValue(order, out var existing)) { var row = (OrderData)existing; // refresh, keep row identity row.Description = fresh.Description; row.TotalInCents = fresh.TotalInCents; row.ClientId = fresh.ClientId; return; } var id = Guid.NewGuid(); fresh.Id = order.Id = id; // write-back identity fresh.DbContext = this; _orders.Add(fresh); _byRef[order] = fresh; } // ── query helpers ─────────────────────────────────────────────── /// Search the per-type table for a record whose predicate matches. public T? Find(Func predicate) where T : DbBase => Set().FirstOrDefault(predicate); // ── tracking / status ─────────────────────────────────────────── /// True if has been saved through this context. public bool IsTracked(object entity) => _byRef.ContainsKey(entity); /// All data records this context currently manages. public IReadOnlyList Tracked => _byRef.Values.ToList(); }