Suche…


Syntax

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

Parameter

Parameter Einzelheiten
cnn Ihre Datenbankverbindung, die bereits geöffnet sein muss.
sql Befehl zum Ausführen
Typen Array von Typen im Datensatz.
Karte Func<> , die die Konstruktion des Rückgabeergebnisses übernimmt.
param Objekt, aus dem Parameter extrahiert werden sollen.
Transaktion Transaktion, zu der diese Abfrage gehört, sofern vorhanden.
gepuffert Ob das Lesen der Ergebnisse der Abfrage zwischengespeichert werden soll oder nicht. Dies ist ein optionaler Parameter mit der Standardeinstellung true. Wenn gepuffert wahr ist, werden die Ergebnisse in eine List<T> gepuffert und dann als IEnumerable<T> , das für mehrere Aufzählungen sicher ist. Wenn gepuffert "false" ist, bleibt die SQL-Verbindung offen, bis Sie mit dem Lesen fertig sind, sodass Sie eine einzelne Zeile zur Zeit im Speicher bearbeiten können. Durch mehrere Aufzählungen werden zusätzliche Verbindungen zur Datenbank hergestellt. Zwar ist gepuffertes false sehr effizient, um die Speicherauslastung zu reduzieren, wenn Sie nur sehr kleine Fragmente der zurückgegebenen Datensätze verwalten, was jedoch einen erheblichen Performance-Overhead im Vergleich zu eifrigem Ergebnis bewirkt. Wenn Sie über zahlreiche gleichzeitige ungepufferte SQL-Verbindungen verfügen, müssen Sie bedenken, dass der Verbindungspool verhungert, dass Anforderungen blockiert werden, bis Verbindungen verfügbar werden.
aufgeteilt auf Das Feld, aus dem wir das zweite Objekt teilen und lesen sollen (Standard: id). Dies kann eine durch Kommas getrennte Liste sein, wenn in einem Datensatz mehr als ein Typ enthalten ist.
commandTimeout Anzahl Sekunden vor der Befehlsausführung.
Befehlstyp Ist es eine gespeicherte Prozedur oder eine Charge?

Einfaches Multi-Table-Mapping

Nehmen wir an, wir haben eine Abfrage der verbleibenden Reiter, die eine Person-Klasse füllen müssen.

Name Geboren Residenz
Daniel Dennett 1942 vereinigte Staaten von Amerika
Sam Harris 1967 vereinigte Staaten von Amerika
Richard Dawkins 1941 Großbritannien
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; }
}

Wir können sowohl die Personenklasse als auch die Residence-Eigenschaft mit einer Instanz von Country mit einer Überlast- Query<> Func<> , die eine Func<> verwendet, die zum Func<> der zurückgegebenen Instanz verwendet werden kann. Die Func<> kann bis zu 7 Eingabetypen aufnehmen, wobei das generische Argument immer der Rückgabetyp ist.

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

Beachten Sie die Verwendung des Arguments splitOn: "Residence" , bei dem es sich um die 1. Spalte der nächsten zu splitOn: "Residence" Klassentypen handelt (in diesem Fall Country ). Dapper sucht automatisch nach einer Spalte mit dem Namen " Id", nach der aufgeteilt werden soll. Wenn jedoch keine splitOn wird und splitOn nicht System.ArgumentException wird, wird eine System.ArgumentException mit einer hilfreichen Nachricht ausgelöst. Obwohl es optional ist, müssen Sie normalerweise einen splitOn Wert splitOn .

One-to-Many-Zuordnung

Schauen wir uns ein komplexeres Beispiel an, das eine Eins-zu-Viele-Beziehung enthält. Unsere Abfrage enthält jetzt mehrere Zeilen, die doppelte Daten enthalten, und wir müssen dies behandeln. Wir machen das mit einem Lookup in einem Abschluss.

Die Abfrage ändert sich geringfügig wie die Beispielklassen.

Ich würde Name Geboren CountryId Ländername BookId BookName
1 Daniel Dennett 1942 1 vereinigte Staaten von Amerika 1 Brainstorms
1 Daniel Dennett 1942 1 vereinigte Staaten von Amerika 2 Spielraum
2 Sam Harris 1967 1 vereinigte Staaten von Amerika 3 Die moralische Landschaft
2 Sam Harris 1967 1 vereinigte Staaten von Amerika 4 Aufwachen: Ein Leitfaden für Spiritualität ohne Religion
3 Richard Dawkins 1941 2 Großbritannien 5 Die Magie der Realität: Wie wir wissen, was wirklich wahr ist
3 Richard Dawkins 1941 2 Großbritannien 6 Appetit for Wonder: Die Entstehung eines Wissenschaftlers
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; }
}

Das remainingHorsemen WörterbuchHorsemen wird mit vollständig materialisierten Instanzen der Personenobjekte gefüllt. Für jede Zeile des Abfrageergebnisses werden die zugeordneten Werte von Instanzen der in den Lambda-Argumenten definierten Typen übergeben, und es liegt an Ihnen, wie Sie damit umgehen.

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

Beachten Sie, dass das splitOn Argument eine durch Kommas getrennte Liste der ersten Spalten des nächsten Typs ist.

Mapping von mehr als 7 Typen

Manchmal überschreitet die Anzahl der Typen, die Sie abbilden, die Zahl 7, die von der Funktion <> zur Verfügung gestellt wird, die die Konstruktion durchführt.

Anstelle der Query<> mit den generischen Typargumenteingaben werden die Typen bereitgestellt, denen ein Array zugeordnet werden soll, gefolgt von der Mapping-Funktion. Abgesehen von der anfänglichen manuellen Einstellung und dem Umwandeln der Werte ändert sich der Rest der Funktion nicht.

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

Benutzerdefinierte Zuordnungen

Wenn die Abfragespaltennamen nicht mit Ihren Klassen übereinstimmen, können Sie Zuordnungen für Typen einrichten. In diesem Beispiel wird die Zuordnung mit System.Data.Linq.Mapping.ColumnAttribute sowie eine benutzerdefinierte Zuordnung System.Data.Linq.Mapping.ColumnAttribute .

Die Zuordnungen müssen nur einmal pro Typ eingerichtet werden. Setzen Sie sie also beim Start der Anwendung oder an anderer Stelle so, dass sie nur einmal initialisiert werden.

Nehmen wir wieder dieselbe Abfrage wie das One-to-Many-Beispiel an, und die Klassen werden wie folgt auf bessere Namen umgestaltet:

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

Beachten Sie, dass sich Book nicht auf ColumnAttribute , wir müssen jedoch die if ColumnAttribute beibehalten

Platzieren Sie diesen Mapping-Code nun irgendwo in Ihrer Anwendung, wo er nur einmal ausgeführt wird:

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

Dann wird die Abfrage mit einem der vorherigen Beispiele Query<> .

Eine einfachere Möglichkeit zum Hinzufügen der Zuordnungen wird in dieser Antwort gezeigt .



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow