Ricerca…


Sintassi

  • 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)

Parametri

Parametro Dettagli
cnn La tua connessione al database, che deve essere già aperta.
sql Comando da eseguire.
tipi Matrice di tipi nel set di record.
carta geografica Func<> che gestisce la costruzione del risultato di ritorno.
param Oggetto da cui estrarre i parametri.
transazione Transazione di cui questa query fa parte, se esiste.
tamponata Indica se bufferizzare o meno i risultati della query. Questo è un parametro facoltativo con il valore predefinito true. Quando il buffer è vero, i risultati vengono memorizzati in un List<T> e quindi restituiti come IEnumerable<T> che è sicuro per l'enumerazione multipla. Quando il buffer è falso, la connessione sql viene mantenuta aperta fino al termine della lettura, consentendo di elaborare una singola riga alla volta in memoria. Più enumerazioni genereranno ulteriori connessioni al database. Mentre buffered false è altamente efficiente per ridurre l'utilizzo della memoria se si mantengono solo frammenti molto piccoli dei record restituiti, ha un overhead delle prestazioni considerevole rispetto alla materializzazione impaziente del set di risultati. Infine, se si dispone di numerose connessioni SQL simultanee non bufferizzate, è necessario considerare l'inattività del pool di connessioni che causa il blocco delle richieste fino a quando le connessioni diventano disponibili.
dividersi Il campo dovremmo dividere e leggere il secondo oggetto da (predefinito: id). Questo può essere un elenco delimitato da virgole quando più di 1 tipo è contenuto in un record.
CommandTimeout Numero di secondi prima del timeout dell'esecuzione del comando.
CommandType È un processo memorizzato o un batch?

Semplice mappatura multi-tabella

Diciamo che abbiamo una query sui restanti cavalieri che devono compilare una classe Person.

Nome Nato Residenza
Daniel Dennett 1942 Stati Uniti d'America
Sam Harris 1967 Stati Uniti d'America
Richard Dawkins 1941 Regno Unito
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; }
}

Possiamo popolare la classe persona e la proprietà Residenza con un'istanza di Paese utilizzando una Query<> sovraccarico Query<> che accetta un Func<> che può essere utilizzato per comporre l'istanza restituita. Il Func<> può richiedere fino a 7 tipi di input con l'argomento generico finale che è sempre il tipo restituito.

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

Notare l'uso dello splitOn: "Residence" argomento splitOn: "Residence" che è la prima colonna del prossimo tipo di classe da compilare (in questo caso Country ). Dapper cercherà automaticamente una colonna denominata Id da dividere, ma se non ne trova una e splitOn non viene fornito un System.ArgumentException verrà lanciato con un messaggio utile. Quindi, sebbene sia facoltativo, di solito devi fornire un valore splitOn .

Mappatura uno-a-molti

Diamo un'occhiata a un esempio più complesso che contiene una relazione uno-a-molti. La nostra query ora conterrà più righe contenenti dati duplicati e dovremo gestirli. Lo facciamo con una ricerca in una chiusura.

La query cambia leggermente come fanno le classi di esempio.

Id Nome Nato CountryId Nome del paese BookID BookName
1 Daniel Dennett 1942 1 Stati Uniti d'America 1 scambi di idee
1 Daniel Dennett 1942 1 Stati Uniti d'America 2 Camera di gomito
2 Sam Harris 1967 1 Stati Uniti d'America 3 Il paesaggio morale
2 Sam Harris 1967 1 Stati Uniti d'America 4 Svegliarsi: una guida alla spiritualità senza religione
3 Richard Dawkins 1941 2 Regno Unito 5 La magia della realtà: come sappiamo che cosa è veramente vero
3 Richard Dawkins 1941 2 Regno Unito 6 An Appetite for Wonder: The Making of a Scientist
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; }
}

Il dizionario remainingHorsemen sarà popolato con istanze completamente materializzate degli oggetti persona. Per ogni riga del risultato della query vengono passati i valori mappati delle istanze dei tipi definiti negli argomenti lambda e sta a te decidere come gestirlo.

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

Si noti come l'argomento splitOn è un elenco delimitato da virgole delle prime colonne del prossimo tipo.

Mappatura di oltre 7 tipi

A volte il numero di tipi che stai mappando supera il 7 fornito da Func <> che esegue la costruzione.

Invece di usare la Query<> con gli input dell'argomento di tipo generico, forniremo i tipi da mappare come una matrice, seguita dalla funzione di mappatura. A parte l'impostazione manuale iniziale e la trasmissione dei valori, il resto della funzione non 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");

Mappature personalizzate

Se i nomi delle colonne delle query non corrispondono alle tue classi, puoi configurare i mapping per i tipi. Questo esempio dimostra la mappatura utilizzando System.Data.Linq.Mapping.ColumnAttribute e una mappatura personalizzata.

Le mappature devono essere configurate solo una volta per tipo, quindi impostarle all'avvio dell'applicazione o da qualche altra parte in cui vengono inizializzate solo una volta.

Assumendo di nuovo la stessa query dell'esempio uno a molti e le classi refactored verso nomi migliori come:

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; }
}

Nota come Book non si basa su ColumnAttribute ma dovremmo mantenere la dichiarazione if

Ora posiziona questo codice di mappatura da qualche parte nell'applicazione in cui viene eseguito solo una volta:

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);

Quindi la query viene eseguita utilizzando uno degli esempi precedenti Query<> .

In questa risposta viene mostrato un modo più semplice per aggiungere i mapping.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow