C#:asigne los resultados de la consulta a varios objetos con Dapper

C#:asigne los resultados de la consulta a varios objetos con Dapper

Cuando consulta tablas unidas, puede asignar cada fila a varios objetos mediante la función de asignación múltiple en Dapper.

Para el mapa múltiple, debe proporcionar a Dapper lo siguiente:

  • A qué tipos asignar.
  • En qué columna(s) dividir. Esto le dice a Dapper qué columnas debe intentar asignar a qué tipo.
  • Una función de mapeo en la que Dapper pasa los objetos mapeados y usted puede vincularlos.

En este artículo, mostraré ejemplos de mapeo múltiple.

Nota:si no especifica la columna dividida, usará el valor predeterminado de "Id". Recomiendo siempre especificar explícitamente la columna dividida.

Asignación múltiple de relación uno a uno

La tabla Pedidos tiene una relación de uno a uno con la tabla Clientes y están vinculados por la columna CustomerId:

La siguiente consulta SQL selecciona un pedido y el cliente asociado:

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)

Estos son los resultados de la consulta:

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

Para asignar estos resultados a un objeto de pedido y cliente, use asignación múltiple y división en la columna CustomerId:

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 significa primero asignar las columnas a un objeto Pedido, luego a un objeto Cliente y devolver IEnumerable.

Para cada fila, crea un objeto Pedido y un objeto Cliente. Asigna las columnas a los objetos en función de la columna dividida (CustomerId) de esta manera:

  • Columnas de pedido =todas las columnas a la izquierda de CustomerId (OrderId, Estado).
  • Columnas de clientes =columnas restantes (Id. de cliente, Nombre).

El resultado final es un objeto Pedido con un objeto Cliente:

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

Asignación múltiple de relación de uno a muchos

La tabla Pedidos tiene una relación de uno a varios con la tabla Líneas de pedidos, y están vinculados por la columna OrderId:

La siguiente consulta SQL selecciona pedidos y líneas de pedido asociadas:

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)

Estos son los resultados de la consulta (para una sola identificación de pedido):

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)

Para asignar estos resultados a objetos Order/OrderLine, asigne múltiples y divida en la columna OrderLineId. La función de mapa es más compleja en el escenario de uno a muchos.

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 significa primero asignar las columnas a un objeto Order, luego a un objeto OrderLine y devolver IEnumerable.

Para cada fila, crea un objeto Order y un objeto OrderLine y asigna las columnas en función de la columna dividida (OrderLineId) de esta manera:

  • Columnas de pedido =todas las columnas a la izquierda de OrderLineId (OrderId, Estado).
  • Columnas de línea de pedido =columnas restantes (ID de línea de pedido, producto, cantidad).

Pasa los objetos mapeados a la función map. Dapper asigna las columnas de pedido a un nuevo objeto de pedido para cada fila, razón por la cual debe desduplicar y realizar un seguimiento de los objetos de pedido únicos con un diccionario.

Esto da como resultado el siguiente objeto Order con una matriz de objetos OrderLine:

{
  "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)

Nota:Parece ineficiente que Dapper esté asignando las columnas de pedido a nuevos objetos de pedido para cada fila. La alternativa es ejecutar varias consultas, una para Pedidos y otra para Líneas de pedido, y luego recorrer los resultados y vincularlos. Según mis pruebas, tiene aproximadamente el mismo rendimiento que el mapeo múltiple.

Asignación múltiple a más de dos objetos

La tabla Pedidos tiene una relación de uno a uno con la tabla Clientes y la tabla Tiendas:

La siguiente consulta SQL selecciona un pedido y un cliente y una tienda asociados:

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)

Aquí están los resultados:

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

A continuación se explica cómo asignar varios resultados a un objeto Pedido/Cliente/Tienda:

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 significa primero asignar las columnas a un objeto Pedido, luego a un objeto Cliente, luego a un objeto Tienda y finalmente devolver IEnumerable.

Cuando está asignando a más de dos objetos, deberá especificar varias columnas divididas con una cadena delimitada por comas ("CustomerId, StoreId"). Asigna las columnas a los tres objetos en función de estas columnas divididas (CustomerId y StoreId) de esta manera:

  • Columnas de pedido =todas las columnas a la izquierda de CustomerId (OrderId, Estado).
  • Columnas de clientes =columnas restantes a la izquierda de StoreId (CustomerId, Name).
  • Columnas de la tienda =columnas restantes (StoreId, Ubicación).

Aquí está el objeto Pedido resultante con objetos Cliente/Tienda vinculados:

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