Lessons in this course (8)

4. Multi-mapping — joins into object graphs

Intermediate★ Must knowLesson 4 of 8
csharp
public class Order { public int Id { get; set; } public DateTime OrderDate { get; set; } public Customer Customer { get; set; } = null!; public List<OrderLine> Lines { get; } = []; }
public class Customer { public int Id { get; set; } public string Name { get; set; } = ""; }
public class OrderLine { public int Id { get; set; } public string Product { get; set; } = ""; public int Qty { get; set; } }

// One-to-one: Order + Customer
var sql = @"SELECT o.Id, o.OrderDate, c.Id, c.Name
            FROM dbo.Orders o JOIN dbo.Customers c ON c.Id = o.CustomerId";
var orders = await conn.QueryAsync<Order, Customer, Order>(sql,
    (order, customer) => { order.Customer = customer; return order; },
    splitOn: "Id");          // where the second object's columns start (default "Id")

// One-to-many: Orders + Lines — de-duplicate parents with a dictionary
var lookup = new Dictionary<int, Order>();
var sql2 = @"SELECT o.Id, o.OrderDate, l.Id, l.Product, l.Qty
             FROM dbo.Orders o JOIN dbo.OrderLines l ON l.OrderId = o.Id
             WHERE o.CustomerId = @cid";
await conn.QueryAsync<Order, OrderLine, Order>(sql2, (o, l) =>
{
    if (!lookup.TryGetValue(o.Id, out var existing)) lookup[o.Id] = existing = o;
    existing.Lines.Add(l);
    return existing;
}, new { cid = 42 }, splitOn: "Id");
var result = lookup.Values.ToList();

splitOn names the first column of each subsequent object; if your second table's key isn't called Id, pass its column name (e.g. splitOn: "CustomerId").