Saltar a contenido

Dapper

¿Qué es Dapper?

Dapper es un mapeador de objetos simple para .NET y posee el título de King of Micro ORM en términos de velocidad y es virtualmente tan rápido como usar un lector de datos ADO.NET sin procesar. Un ORM es un mapeador relacional de objetos, que es responsable del mapeo entre la base de datos y el lenguaje de programación.

Dapper extiende IDbConnection al proporcionar métodos de extensión útiles para consultar su base de datos.

Para más info consultar

Ejemplo de Uso

Consulta tipada

La consulta SQL sin formato se puede ejecutar utilizando el método de consulta y asignar el resultado a una lista fuertemente tipada.

string sql = "SELECT TOP 10 * FROM OrderDetails";

using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
    var orderDetails = connection.Query<OrderDetail>(sql).ToList();

    Console.WriteLine(orderDetails.Count);

    FiddleHelper.WriteTable(orderDetails);
}

A tener en cuenta:

  • El método Query o QueryAsync siempre devuelve un IEnumerable, por más que en la base exista un solo elemento.
  • El método Query o QueryAsync intenta mapear el nombre de las columnas del resultset con el nombre de los atributos del objeto POCO definido en los tag (<OrderDetail>).
  • Recomendamos siempre utilizar los métodos async.

Obtener el primer valor

string sql = "SELECT * FROM OrderDetails WHERE OrderDetailID = @OrderDetailID;";

using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
    var orderDetail = connection.QueryFirstOrDefault<OrderDetail>(sql, new {OrderDetailID = 1});

    FiddleHelper.WriteTable(new List<OrderDetail>() { orderDetail });
}

A tener en cuenta:

  • El método QueryFirstOrDefault o QueryFirstOrDefaultAsync devuelve siempre un solo objeto mapeado, en caso de no encontrarse al menos un registro de la consulta, devolvera null.

Asignación múltiple (uno a uno)

La consulta SQL sin formato se puede ejecutar utilizando el método de consulta y asignar el resultado a una lista fuertemente tipada con una relación uno a uno.

string sql = "SELECT * FROM Invoice AS A INNER JOIN InvoiceDetail AS B ON A.InvoiceID = B.InvoiceID;";

using (var connection = My.ConnectionFactory())
{
    connection.Open();

    var invoices = connection.Query<Invoice, InvoiceDetail, Invoice>(
            sql,
            (invoice, invoiceDetail) =>
            {
                invoice.InvoiceDetail = invoiceDetail;
                return invoice;
            },
            splitOn: "InvoiceID")
        .Distinct()
        .ToList();
}

A tener en cuenta:

  • El método QueryFirstOrDefault o QueryFirstOrDefaultAsync devuelve siempre un solo objeto mapeado, en caso de no encontrarse al menos un registro de la consulta, devolvera null.

Asignación múltiple (uno a varios)

La consulta SQL sin formato se puede ejecutar utilizando el método de consulta y asignar el resultado a una lista fuertemente tipada con relaciones de uno a muchos.

string sql = "SELECT TOP 10 * FROM Orders AS A INNER JOIN OrderDetails AS B ON A.OrderID = B.OrderID;";

using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
{
    var orderDictionary = new Dictionary<int, Order>();

    // <PrimerObjeto, SegundoObjeto, Response>
    var list = connection.Query<Order, OrderDetail, Order>(
    sql,
    (order, orderDetail) =>
    {
        Order orderEntry;

        if (!orderDictionary.TryGetValue(order.OrderID, out orderEntry))
        {
        orderEntry = order;
        orderEntry.OrderDetails = new List<OrderDetail>();
        orderDictionary.Add(orderEntry.OrderID, orderEntry);
        }

        orderEntry.OrderDetails.Add(orderDetail);
        return orderEntry;
    },
    splitOn: "OrderID")
    .Distinct()
    .ToList();

    Console.WriteLine(list.Count);

    FiddleHelper.WriteTable(list);
    FiddleHelper.WriteTable(list.First().OrderDetails);
}

A tener en cuenta:

  • El método Query<Tfirst, Tsecond, Response> acepta hasta 6 objetos anidados.

Otra forma menos elegante y eficiente, pero más simple es generar un objeto POCO con valores de primer orden y luego realizar el mapping manualmente.

public class InvoiceResulset
{
    public int InvoiceID {get;set;}
    public string AnyColumn {get;set;}
    public string Detail {get;set;}
}
string sql = "SELECT A.InvoiceID, A.AnyColumn, B.Detail FROM Invoice AS A INNER JOIN InvoiceDetail AS B ON A.InvoiceID = B.InvoiceID;";

using (var connection = My.ConnectionFactory())
{
    connection.Open();

    var invoices = connection.Query<InvoiceResulset>(sql).ToList();

    return invoices.Select(x => (Invoce) x).ToList();
}

SQLBuilder

Dapper SQLBuilder es una librería que nos permite generar querys dinámicas. Recomendamos usar cuando necesitamos generar queries que cambien dependiendo del contexto, por ejemplo, cuando tenemos múltiples filtros opcionales.

public override IEnumerable<Guid> List(
            int maxResults,
            DateTime? start = null,
            DateTime? finish = null,
            ListResultsOrder orderBy = ListResultsOrder.Descending)
        {
            var builder = new SqlBuilder();
            var t = builder.AddTemplate($"select Id from MiniProfilers /**where**/ /**orderby**/ LIMIT({maxResults})");

            if (start != null)
            {
                builder.Where("Started > @start", new { start });
            }
            if (finish != null)
            {
                builder.Where("Started < @finish", new { finish });
            }

            builder.OrderBy(orderBy == ListResultsOrder.Descending ? "Started desc" : "Started asc");

            using (var conn = GetOpenConnection())
            {
                return conn.Query<Guid>(t.RawSql, t.Parameters).ToList();
            }
        }

Para más info ver Para más ejemplos ver