Buscar..


Sintaxis

  • public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>( this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
  • public static IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
  • public static IEnumerable<TReturn> Query<TReturn>(this IDbConnection cnn, string sql, Type[] types, Func<object[], TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)

Parámetros

Parámetro Detalles
cnn Su conexión de base de datos, que ya debe estar abierta.
sql Comando para ejecutar.
tipos Conjunto de tipos en el conjunto de registros.
mapa Func<> que maneja la construcción del resultado de retorno.
param Objeto para extraer parámetros de.
transacción Transacción de la que forma parte esta consulta, si la hubiera.
amortiguado Ya sea para almacenar o no los resultados de la consulta. Este es un parámetro opcional con el valor predeterminado verdadero. Cuando el búfer es verdadero, los resultados se guardan en una List<T> y luego se devuelven como IEnumerable<T> que es seguro para la enumeración múltiple. Cuando el búfer es falso, la conexión SQL se mantiene abierta hasta que finalice la lectura, lo que le permite procesar una sola fila en el momento en la memoria. Las enumeraciones múltiples generarán conexiones adicionales a la base de datos. Si bien el búfer falso es altamente eficiente para reducir el uso de memoria, si solo mantiene fragmentos muy pequeños de los registros devueltos, tiene una sobrecarga de rendimiento considerable en comparación con materializar con impaciencia el conjunto de resultados. Por último, si tiene numerosas conexiones de SQL no búfer concurrentes, debe considerar que la inanición de la agrupación de conexiones provoca que las solicitudes se bloqueen hasta que las conexiones estén disponibles.
dividido en El campo del que deberíamos dividir y leer el segundo objeto (por defecto: id). Esta puede ser una lista delimitada por comas cuando más de 1 tipo está contenido en un registro.
commandTimeout Número de segundos antes del tiempo de espera de ejecución del comando.
commandType ¿Es un proceso almacenado o un lote?

Mapeo simple multi-mesa

Digamos que tenemos una consulta de los jinetes restantes que necesitan poblar una clase de Persona.

Nombre Nacido Residencia
Daniel Dennett 1942 Estados Unidos de America
Sam Harris 1967 Estados Unidos de America
Richard Dawkins 1941 Reino Unido
public class Person
{
    public string Name { get; set; }
    public int Born { get; set; }
    public Country Residience { get; set; }
}

public class Country
{
    public string Residence { get; set; }
}

Podemos rellenar la clase de persona y la propiedad Residencia con una instancia de País usando una Query<> sobrecarga Query<> que toma una función Func<> que se puede usar para componer la instancia devuelta. El Func<> puede tomar hasta 7 tipos de entrada con el argumento genérico final siempre siendo el tipo de retorno.

var sql = @"SELECT 'Daniel Dennett' AS Name, 1942 AS Born, 'United States of America' AS Residence
UNION ALL SELECT 'Sam Harris' AS Name, 1967 AS Born, 'United States of America' AS Residence
UNION ALL SELECT 'Richard Dawkins' AS Name, 1941 AS Born, 'United Kingdom' AS Residence";

var result = connection.Query<Person, Country, Person>(sql, (person, country) => {
        if(country == null)
        {
            country = new Country { Residence = "" };
        }
        person.Residience = country;
        return person;
    }, 
    splitOn: "Residence");

Tenga en cuenta el uso del splitOn: "Residence" que es la primera columna del siguiente tipo de clase que se completará (en este caso, Country ). Dapper buscará automáticamente una columna llamada Id para dividir, pero si no encuentra una y splitOn no se proporciona, se splitOn una splitOn System.ArgumentException con un mensaje útil. Entonces, aunque es opcional, normalmente tendrá que proporcionar un valor de splitOn .

Mapeo uno a muchos

Veamos un ejemplo más complejo que contiene una relación de uno a varios. Nuestra consulta ahora contendrá varias filas que contienen datos duplicados y tendremos que manejar esto. Hacemos esto con una búsqueda en un cierre.

La consulta cambia ligeramente al igual que las clases de ejemplo.

Carné de identidad Nombre Nacido PaísId Nombre del país BookId Nombre del libro
1 Daniel Dennett 1942 1 Estados Unidos de America 1 Tormentas de ideas
1 Daniel Dennett 1942 1 Estados Unidos de America 2 Cuarto de la esquina
2 Sam Harris 1967 1 Estados Unidos de America 3 El paisaje moral
2 Sam Harris 1967 1 Estados Unidos de America 4 Despertar: una guía a la espiritualidad sin religión
3 Richard Dawkins 1941 2 Reino Unido 5 La magia de la realidad: Cómo sabemos lo que es realmente cierto
3 Richard Dawkins 1941 2 Reino Unido 6 Un apetito por la maravilla: la creación de un científico
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Born { get; set; }
    public Country Residience { get; set; }
    public ICollection<Book> Books { get; set; }
}

public class Country
{
    public int CountryId { get; set; }
    public string CountryName { get; set; }
}

public class Book
{
    public int BookId { get; set; }
    public string BookName { get; set; }
}

Los remainingHorsemen diccionario se rellenarán con instancias completamente materializadas de los objetos personales. Para cada fila del resultado de la consulta, se pasan los valores asignados de las instancias de los tipos definidos en los argumentos lambda y depende de usted cómo manejar esto.

            var sql = @"SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 1 AS BookId, 'Brainstorms' AS BookName
UNION ALL SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 2 AS BookId, 'Elbow Room' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId,  'United States of America' AS CountryName, 3 AS BookId, 'The Moral Landscape' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId,  'United States of America' AS CountryName, 4 AS BookId, 'Waking Up: A Guide to Spirituality Without Religion' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId,  'United Kingdom' AS CountryName, 5 AS BookId, 'The Magic of Reality: How We Know What`s Really True' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId,  'United Kingdom' AS CountryName, 6 AS BookId, 'An Appetite for Wonder: The Making of a Scientist' AS BookName";

var remainingHorsemen = new Dictionary<int, Person>();
connection.Query<Person, Country, Book, Person>(sql, (person, country, book) => {
    //person
    Person personEntity;
    //trip
    if (!remainingHorsemen.TryGetValue(person.Id, out personEntity))
    {
        remainingHorsemen.Add(person.Id, personEntity = person);
    }

    //country
    if(personEntity.Residience == null)
    {
        if (country == null)
        {
            country = new Country { CountryName = "" };
        }
        personEntity.Residience = country;
    }                    

    //books
    if(personEntity.Books == null)
    {
        personEntity.Books = new List<Book>();
    }

    if (book != null)
    {
        if (!personEntity.Books.Any(x => x.BookId == book.BookId))
        {
            personEntity.Books.Add(book);
        }
    }

    return personEntity;
}, 
splitOn: "CountryId,BookId");

Observe que el argumento splitOn es una lista delimitada por comas de las primeras columnas del siguiente tipo.

Mapeo de más de 7 tipos

A veces, el número de tipos que está asignando excede los 7 proporcionados por la Función <> que hace la construcción.

En lugar de utilizar la Query<> con las entradas de argumento de tipo genérico, proporcionaremos los tipos para asignar como una matriz, seguido por la función de asignación. Aparte del ajuste manual inicial y la conversión de los valores, el resto de la función no cambia.

            var sql = @"SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 1 AS BookId, 'Brainstorms' AS BookName
UNION ALL SELECT 1 AS Id, 'Daniel Dennett' AS Name, 1942 AS Born, 1 AS CountryId, 'United States of America' AS CountryName, 2 AS BookId, 'Elbow Room' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId,  'United States of America' AS CountryName, 3 AS BookId, 'The Moral Landscape' AS BookName
UNION ALL SELECT 2 AS Id, 'Sam Harris' AS Name, 1967 AS Born, 1 AS CountryId,  'United States of America' AS CountryName, 4 AS BookId, 'Waking Up: A Guide to Spirituality Without Religion' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId,  'United Kingdom' AS CountryName, 5 AS BookId, 'The Magic of Reality: How We Know What`s Really True' AS BookName
UNION ALL SELECT 3 AS Id, 'Richard Dawkins' AS Name, 1941 AS Born, 2 AS CountryId,  'United Kingdom' AS CountryName, 6 AS BookId, 'An Appetite for Wonder: The Making of a Scientist' AS BookName";

var remainingHorsemen = new Dictionary<int, Person>();
connection.Query<Person>(sql,
    new[]
    {
        typeof(Person),
        typeof(Country),
        typeof(Book)
    }
    , obj => {

        Person person = obj[0] as Person;
        Country country = obj[1] as Country;
        Book book = obj[2] as Book;

        //person
        Person personEntity;
        //trip
        if (!remainingHorsemen.TryGetValue(person.Id, out personEntity))
        {
            remainingHorsemen.Add(person.Id, personEntity = person);
        }

        //country
        if(personEntity.Residience == null)
        {
            if (country == null)
            {
                country = new Country { CountryName = "" };
            }
            personEntity.Residience = country;
        }                    

        //books
        if(personEntity.Books == null)
        {
            personEntity.Books = new List<Book>();
        }

        if (book != null)
        {
            if (!personEntity.Books.Any(x => x.BookId == book.BookId))
            {
                personEntity.Books.Add(book);
            }
        }

        return personEntity;
},
splitOn: "CountryId,BookId");

Asignaciones personalizadas

Si los nombres de las columnas de consulta no coinciden con sus clases, puede configurar asignaciones para tipos. Este ejemplo muestra la asignación utilizando System.Data.Linq.Mapping.ColumnAttribute , así como una asignación personalizada.

Las asignaciones solo se deben configurar una vez por tipo, así que configúrelas en el inicio de la aplicación o en algún otro lugar para que solo se inicialicen una vez.

Suponiendo de nuevo la misma consulta que el ejemplo Uno a muchos y las clases rediseñadas para obtener mejores nombres como:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Born { get; set; }
    public Country Residience { get; set; }
    public ICollection<Book> Books { get; set; }
}

public class Country
{
    [System.Data.Linq.Mapping.Column(Name = "CountryId")]
    public int Id { get; set; }

    [System.Data.Linq.Mapping.Column(Name = "CountryName")]
    public string Name { get; set; }
}

public class Book
{
    public int Id { get; set; }

    public string Name { get; set; }
}

Observe cómo Book no se basa en ColumnAttribute pero tendríamos que mantener la instrucción if

Ahora coloque este código de mapeo en algún lugar de su aplicación donde solo se ejecute una vez:

Dapper.SqlMapper.SetTypeMap(
    typeof(Country),
    new CustomPropertyTypeMap(
        typeof(Country),
        (type, columnName) =>
            type.GetProperties().FirstOrDefault(prop =>
                prop.GetCustomAttributes(false)
                    .OfType<System.Data.Linq.Mapping.ColumnAttribute>()
                    .Any(attr => attr.Name == columnName)))
);


var bookMap = new CustomPropertyTypeMap(
    typeof(Book),
    (type, columnName) =>
    {
        if(columnName == "BookId")
        {
            return type.GetProperty("Id");
        }

        if (columnName == "BookName")
        {
            return type.GetProperty("Name");
        }

        throw new InvalidOperationException($"No matching mapping for {columnName}");
    }        
);
Dapper.SqlMapper.SetTypeMap(typeof(Book), bookMap);

Luego, la consulta se ejecuta utilizando cualquiera de los ejemplos anteriores de Query<> .

Una forma más sencilla de agregar las asignaciones se muestra en esta respuesta .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow