Recherche…


Syntaxe

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

Paramètres

Paramètre Détails
CNN Votre connexion à la base de données, qui doit déjà être ouverte.
sql Commande à exécuter.
les types Tableau de types dans le jeu d'enregistrements.
carte Func<> qui gère la construction du résultat de retour.
param Objet pour extraire les paramètres de.
transaction Transaction dont cette requête fait partie, le cas échéant.
tamponné S'il faut ou non mettre en mémoire tampon les résultats de la requête. Ceci est un paramètre facultatif avec la valeur par défaut étant true. Lorsque la mise en mémoire tampon est vraie, les résultats sont mis en mémoire tampon dans une List<T> , puis renvoyés sous la forme d'un IEnumerable<T> sûr pour une énumération multiple. Lorsque la mise en mémoire tampon est fausse, la connexion SQL est maintenue ouverte jusqu'à ce que vous ayez fini de lire, ce qui vous permet de traiter une seule ligne à la fois en mémoire. Plusieurs énumérations engendreront des connexions supplémentaires à la base de données. Bien que false mis en mémoire tampon soit très efficace pour réduire l’utilisation de la mémoire si vous ne gérez que de très petits fragments d’enregistrements renvoyés, il se caractérise par une surcharge de performances considérable par rapport à la matérialisation rapide du jeu de résultats. Enfin, si vous avez de nombreuses connexions SQL non tamponnées simultanées, vous devez tenir compte de la famine du pool de connexions, ce qui entraîne le blocage des requêtes jusqu'à ce que les connexions soient disponibles.
splitOn Le champ que nous devons diviser et lire le second objet (par défaut: id). Cela peut être une liste délimitée par des virgules lorsque plus d'un type est contenu dans un enregistrement.
commandeTimeout Nombre de secondes avant l'expiration du délai d'exécution de la commande.
type de commande Est-ce un processus stocké ou un lot?

Mappage multi-tables simple

Disons que nous avons une interrogation des cavaliers restants qui doivent remplir une classe de personnes.

prénom Née Résidence
Daniel Dennett 1942 les États-Unis d'Amérique
Sam Harris 1967 les États-Unis d'Amérique
Richard dawkins 1941 Royaume-Uni
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; }
}

Nous pouvons remplir la classe de personne ainsi que la propriété Residence avec une instance de Country à l'aide d'une Query<> surcharge Query<> qui prend un Func<> pouvant être utilisé pour composer l'instance renvoyée. Le Func<> peut prendre jusqu'à 7 types d'entrées, l'argument générique final étant toujours le type de retour.

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

Notez l'utilisation de l' splitOn: "Residence" qui est la 1ère colonne du prochain type de classe à renseigner (dans ce cas, Country ). Dapper recherchera automatiquement une colonne appelée Id à diviser mais si elle n'en trouve pas et que splitOn n'est pas fourni, une System.ArgumentException sera lancée avec un message utile. Donc, bien que ce soit facultatif, vous devrez généralement fournir une valeur splitOn .

Cartographie un à plusieurs

Regardons un exemple plus complexe qui contient une relation un-à-plusieurs. Notre requête contiendra désormais plusieurs lignes contenant des données en double et nous devrons gérer cela. Nous faisons cela avec une recherche dans une fermeture.

La requête change légèrement comme le font les exemples de classes.

Id prénom Née CountryId Nom du pays BookId Nom du livre
1 Daniel Dennett 1942 1 les États-Unis d'Amérique 1 Brainstorms
1 Daniel Dennett 1942 1 les États-Unis d'Amérique 2 Espace vital
2 Sam Harris 1967 1 les États-Unis d'Amérique 3 Le paysage moral
2 Sam Harris 1967 1 les États-Unis d'Amérique 4 Se réveiller: Guide de spiritualité sans religion
3 Richard dawkins 1941 2 Royaume-Uni 5 La magie de la réalité: comment nous savons ce qui est vraiment vrai
3 Richard dawkins 1941 2 Royaume-Uni 6 Un appétit pour merveille: la fabrication d'un scientifique
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; }
}

Les dictionnaires remainingHorsemen seront remplis d'instances entièrement matérialisées des objets de la personne. Pour chaque ligne du résultat de la requête, les valeurs mappées des instances des types définis dans les arguments lambda sont transmises et il vous appartient de gérer cela.

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

Notez comment l'argument splitOn est une liste délimitée par des virgules des premières colonnes du type suivant.

Cartographie de plus de 7 types

Parfois, le nombre de types que vous mappez dépasse les 7 fournis par le Func <> qui effectue la construction.

Au lieu d'utiliser la Query<> avec les entrées d'argument de type générique, nous allons fournir les types à mapper en tant que tableau, suivis de la fonction de mappage. Outre le réglage manuel initial et la conversion des valeurs, le reste de la fonction ne change pas.

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

Mappages personnalisés

Si les noms des colonnes de requête ne correspondent pas à vos classes, vous pouvez configurer des mappages pour les types. Cet exemple illustre le mappage à l'aide de System.Data.Linq.Mapping.ColumnAttribute ainsi qu'un mappage personnalisé.

Les mappages ne doivent être configurés qu'une seule fois par type, donc définissez-les au démarrage de l'application ou ailleurs pour qu'ils ne soient initialisés qu'une seule fois.

En supposant la même requête que l'exemple One-to-many et les classes remaniées vers de meilleurs noms comme ceux-ci:

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

Notez que Book ne repose pas sur ColumnAttribute mais que nous devons conserver l'instruction if

Placez maintenant ce code de mappage quelque part dans votre application où il n'est exécuté qu'une seule fois:

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

La requête est ensuite exécutée à l'aide de l'un des exemples de Query<> précédents Query<> .

Une manière plus simple d'ajouter les mappages est montrée dans cette réponse .



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow