C# – Ordnen Sie mit Dapper Abfrageergebnisse mehreren Objekten zu

C# – Ordnen Sie mit Dapper Abfrageergebnisse mehreren Objekten zu

Wenn Sie verbundene Tabellen abfragen, können Sie jede Zeile mehreren Objekten zuordnen, indem Sie die Multi-Mapping-Funktion in Dapper verwenden.

Um mehrere Karten zu erstellen, müssen Sie Dapper Folgendes bereitstellen:

  • Welche Typen zugeordnet werden sollen.
  • Welche Spalte(n) geteilt werden sollen. Dies teilt Dapper mit, welche Spalten es versuchen soll, welchem ​​Typ zuzuordnen.
  • Eine Zuordnungsfunktion, bei der Dapper die zugeordneten Objekte übergibt und Sie sie miteinander verknüpfen können.

In diesem Artikel zeige ich Beispiele für Multi-Mapping.

Hinweis:Wenn Sie die geteilte Spalte nicht angeben, wird die Standardeinstellung „Id“ verwendet. Ich empfehle, die Split-Spalte immer explizit anzugeben.

Eins-zu-Eins-Beziehungs-Multi-Mapping

Die Orders-Tabelle hat eine Eins-zu-eins-Beziehung mit der Customers-Tabelle, und sie sind durch die CustomerId-Spalte verknüpft:

Die folgende SQL-Abfrage wählt eine Bestellung und den zugehörigen Kunden aus:

SELECT o.OrderId, o.[Status], c.CustomerId, c.[Name]                         
FROM Orders o
INNER JOIN Customers c
ON o.CustomerId = c.CustomerId
WHERE o.OrderId = @Id
Code language: SQL (Structured Query Language) (sql)

Hier sind die Ergebnisse der Abfrage:

OrderId	Status	CustomerId	Name
43672	New	30067		Corey LuoCode language: plaintext (plaintext)

Um diese Ergebnisse einem Order- und Customer-Objekt zuzuordnen, verwenden Sie Multi-Mapping und splitten in der CustomerId-Spalte:

using (var con = new SqlConnection(ConnectionString))
{
	return con.Query<Order, Customer, Order>(GET_SQL, 
		map: (order, customer) =>
		{
			order.Customer = customer;
			return order;
		},
		param: new { id },
		splitOn: "CustomerId").FirstOrDefault();
}
Code language: C# (cs)

Query bedeutet, die Spalten zuerst einem Order-Objekt und dann einem Customer-Objekt zuzuordnen und IEnumerable.

zurückzugeben

Für jede Zeile erstellt es ein Order-Objekt und ein Customer-Objekt. Es ordnet die Spalten den Objekten basierend auf der geteilten Spalte (CustomerId) wie folgt zu:

  • Auftragsspalten =alle Spalten links von CustomerId (OrderId, Status).
  • Kundenspalten =verbleibende Spalten (CustomerId, Name).

Das Endergebnis ist ein Order-Objekt mit einem Customer-Objekt:

{
  "OrderId": 43659,
  "Customer": {
    "CustomerId": 29825,
    "Name": "Corey Luo"
  },
  "Status": "New"
}Code language: JSON / JSON with Comments (json)

Eins-zu-Viele-Beziehungs-Multi-Mapping

Die Orders-Tabelle hat eine 1:n-Beziehung mit der OrderLines-Tabelle, und sie sind durch die OrderId-Spalte verknüpft:

Die folgende SQL-Abfrage wählt Bestellungen und zugehörige Bestellpositionen aus:

SELECT o.OrderId, o.Status, ol.OrderLineId, ol.Product, ol.Quantity
FROM Orders o
INNER JOIN OrderLines ol
ON o.OrderId = ol.OrderId
WHERE o.OrderId IN @Ids
Code language: SQL (Structured Query Language) (sql)

Hier sind die Abfrageergebnisse (für eine einzelne Bestell-ID):

OrderId	Status	OrderLineId	Product				Quantity
43672	New	126		Mountain Bike Socks, M		6
43672	New	127		Mountain-100 Black, 42		2
43672	New	128		Mountain-100 Silver, 48		1Code language: plaintext (plaintext)

Um diese Ergebnisse Order/OrderLine-Objekten zuzuordnen, führen Sie eine Mehrfachzuordnung durch und teilen Sie sie in der OrderLineId-Spalte auf. Die Kartenfunktion ist im One-to-Many-Szenario komplexer.

var orderMap = new Dictionary<int, Order>();

using (var con = new SqlConnection(ConnectionString))
{
	con.Query<Order, OrderLine, Order>(GET_LINES_SQL,
		map: (order, orderLine) =>
		{
			orderLine.OrderId = order.OrderId; //non-reference back link

			//check if this order has been seen already
			if (orderMap.TryGetValue(order.OrderId, out Order existingOrder))
			{
				order = existingOrder;
			}
			else
			{
				order.Lines = new List<OrderLine>();
				orderMap.Add(order.OrderId, order);

			}

			order.Lines.Add(orderLine);
			return order;
		},
		splitOn: "OrderLineId",
		param: new { ids }
	);
}

return orderMap.Values;
Code language: C# (cs)

Query bedeutet, die Spalten zuerst einem Order-Objekt und dann einem OrderLine-Objekt zuzuordnen und IEnumerable.

zurückzugeben

Für jede Zeile erstellt es ein Order-Objekt und ein OrderLine-Objekt und ordnet die Spalten basierend auf der geteilten Spalte (OrderLineId) wie folgt zu:

  • Auftragsspalten =alle Spalten links von OrderLineId (OrderId, Status).
  • OrderLine-Spalten =verbleibende Spalten (OrderLineId, Product, Quantity).

Sie übergibt die abgebildeten Objekte an die Kartenfunktion. Dapper ordnet die Auftragsspalten für jede Zeile einem neuen Auftragsobjekt zu – weshalb Sie eindeutige Auftragsobjekte mit einem Wörterbuch deduplizieren und verfolgen müssen.

Dies führt zu folgendem Order-Objekt mit einem Array von OrderLine-Objekten:

{
  "OrderId": 43672,
  "Lines": [
    {
      "OrderLineId": 126,
      "OrderId": 43672,
      "Product": "Mountain Bike Socks, M",
      "Quantity": 6
    },
    {
      "OrderLineId": 127,
      "OrderId": 43672,
      "Product": "Mountain-100 Black, 42",
      "Quantity": 2
    },
    {
      "OrderLineId": 128,
      "OrderId": 43672,
      "Product": "Mountain-100 Silver, 48",
      "Quantity": 1
    }
  ],
  "Status": "New"
}Code language: JSON / JSON with Comments (json)

Hinweis:Es scheint ineffizient zu sein, dass Dapper die Auftragsspalten für jede Zeile auf neue Auftragsobjekte abbildet. Die Alternative besteht darin, mehrere Abfragen auszuführen – eine für Orders und eine für OrderLines – und dann die Ergebnisse zu durchlaufen und zu verknüpfen. Basierend auf meinen Tests hat das ungefähr die gleiche Leistung wie Multi-Mapping.

Multi-Mapping auf mehr als zwei Objekte

Die Orders-Tabelle hat eine Eins-zu-eins-Beziehung mit der Customers-Tabelle und der Stores-Tabelle:

Die folgende SQL-Abfrage wählt eine Bestellung und den zugehörigen Kunden und das Geschäft aus:

SELECT o.OrderId, o.[Status], c.CustomerId, c.[Name], s.StoreId, s.[Location]
FROM Orders o
INNER JOIN Customers c
ON o.CustomerId = c.CustomerId
INNER JOIN Stores s
ON o.StoreId = s.StoreId
WHERE o.OrderId = @Id
Code language: SQL (Structured Query Language) (sql)

Hier sind die Ergebnisse:

OrderId	Status	CustomerId	Name		StoreId	Location
43672	New	30067		Corey Luo	1	Main StCode language: plaintext (plaintext)

So ordnen Sie diese Ergebnisse einem Order/Customer/Store-Objekt mehrfach zu:

using (var con = new SqlConnection(ConnectionString))
{
	return con.Query<Order, Customer, Store, Order>(GET_SQL,
		map: (order, customer, store) =>
		{
			order.Customer = customer;
			order.Store = store;
			return order;
		},
	param: new { id },
	splitOn: "CustomerId,StoreId").FirstOrDefault();
}
Code language: C# (cs)

Query bedeutet, zuerst die Spalten einem Order-Objekt zuzuordnen, dann einem Customer-Objekt, dann einem Store-Objekt und schließlich IEnumerable zurückzugeben.

Wenn Sie mehr als zwei Objekten zuordnen, müssen Sie mehrere geteilte Spalten mit einer durch Kommas getrennten Zeichenfolge („CustomerId,StoreId“) angeben. Es ordnet die Spalten den drei Objekten basierend auf diesen geteilten Spalten (CustomerId und StoreId) wie folgt zu:

  • Auftragsspalten =alle Spalten links von CustomerId (OrderId, Status).
  • Kundenspalten =verbleibende Spalten links von StoreId (CustomerId, Name).
  • Store-Spalten =verbleibende Spalten (StoreId, Location).

Hier ist das resultierende Auftragsobjekt mit verknüpften Kunden-/Geschäftsobjekten:

{
  "OrderId": 43659,
  "Customer": {
    "CustomerId": 29825,
    "Name": "Corey Luo"
  },
  "Status": "New",
  "Store": {
    "StoreId": 1,
    "Location": "Main St"
  }
}Code language: JSON / JSON with Comments (json)