Szukaj…


Składnia

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

Parametry

Parametr Detale
cnn Twoje połączenie z bazą danych, które musi być już otwarte.
sql Polecenie do wykonania.
typy Tablica typów w zestawie rekordów.
mapa Func<> który obsługuje konstrukcję wyniku zwrotu.
param Obiekt do wyodrębnienia parametrów.
transakcja Transakcja, której częścią jest to zapytanie, jeśli istnieje.
buforowane Określa, czy buforować odczyt wyników zapytania. Jest to parametr opcjonalny z domyślną wartością true. Gdy buforowane jest true, wyniki są buforowane na List<T> a następnie zwracane jako IEnumerable<T> który jest bezpieczny dla wielokrotnego wyliczania. Gdy buforowane jest false, połączenie sql jest utrzymywane otwarte do czasu zakończenia odczytu, co pozwala na przetworzenie pojedynczego wiersza w pamięci. Wiele wyliczeń spowoduje odrodzenie dodatkowych połączeń z bazą danych. Chociaż buforowane fałsz jest bardzo wydajne w zmniejszaniu zużycia pamięci, jeśli zachowuje się tylko bardzo małe fragmenty zwracanych rekordów, ma znaczny narzut wydajności w porównaniu do chętnego materializowania zestawu wyników. Wreszcie, jeśli masz wiele równoległych niebuforowanych połączeń sql, musisz rozważyć głodzenie puli połączeń, powodując żądania blokowania, aż połączenia będą dostępne.
podzielone na Pole, z którego powinniśmy podzielić i odczytać drugi obiekt (domyślnie: id). Może to być lista rozdzielana przecinkami, gdy w rekordzie znajduje się więcej niż 1 typ.
CommandTimeout Liczba sekund przed upływem limitu czasu wykonywania polecenia.
CommandType Czy to jest przechowywany proc czy partia?

Proste mapowanie wielu tabel

Powiedzmy, że mamy zapytanie o pozostałych jeźdźców, którzy muszą zapełnić klasę Person.

Nazwa Urodzony Rezydencja
Daniel Dennett 1942 r Stany Zjednoczone Ameryki
Sam Harris 1967 Stany Zjednoczone Ameryki
Richard dawkins 1941 Zjednoczone Królestwo
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; }
}

Możemy wypełnić klasę osoby, a także właściwość Rezydencji wystąpieniem Country, używając Query<> przeciążeniowego Query<> które pobiera Func<> którego można użyć do utworzenia zwróconej instancji. Func<> może przyjmować do 7 typów danych wejściowych, przy czym ostateczny ogólny argument zawsze jest typem zwracanym.

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

Zwróć uwagę na użycie argumentu splitOn: "Residence" który jest pierwszą kolumną następnego typu klasy, który należy wypełnić (w tym przypadku Country ). Dapper automatycznie wyszuka kolumnę o nazwie Id do podziału, ale jeśli jej nie znajdzie, a splitOn nie zostanie udostępniony System.ArgumentException zostanie wygenerowany z pomocnym komunikatem. Więc chociaż jest to opcjonalne, zwykle będziesz musiał podać wartość splitOn .

Mapowanie jeden do wielu

Spójrzmy na bardziej złożony przykład, który zawiera relację jeden do wielu. Nasze zapytanie będzie teraz zawierać wiele wierszy zawierających zduplikowane dane i będziemy musieli sobie z tym poradzić. Robimy to z odnośnikiem w zamknięciu.

Zapytanie zmienia się nieznacznie, podobnie jak przykładowe klasy.

ID Nazwa Urodzony CountryId Nazwa kraju BookId BookName
1 Daniel Dennett 1942 r 1 Stany Zjednoczone Ameryki 1 Burze mózgów
1 Daniel Dennett 1942 r 1 Stany Zjednoczone Ameryki 2) Pokój Elbow
2) Sam Harris 1967 1 Stany Zjednoczone Ameryki 3) Krajobraz moralny
2) Sam Harris 1967 1 Stany Zjednoczone Ameryki 4 Waking Up: Przewodnik po duchowości bez religii
3) Richard dawkins 1941 2) Zjednoczone Królestwo 5 Magia rzeczywistości: jak wiemy, co jest naprawdę prawdą
3) Richard dawkins 1941 2) Zjednoczone Królestwo 6 Apetyt na cud: Dokonywanie naukowca
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; }
}

Słownik remainingHorsemen zostanie zapełniony w pełni zmaterializowanymi instancjami obiektów osoby. Dla każdego wiersza wyniku zapytania przekazywane są zmapowane wartości instancji typów zdefiniowanych w argumentach lambda i od Ciebie zależy, jak sobie z tym poradzić.

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

Zwróć uwagę, że argument splitOn jest rozdzielaną przecinkami listą pierwszych kolumn następnego typu.

Mapowanie ponad 7 typów

Czasami liczba typów mapowania przekracza 7 podaną przez Func <>, który wykonuje konstrukcję.

Zamiast używać Query<> z wejściowymi argumentami typu ogólnego, podamy typy do mapowania jako tablicę, a następnie funkcję mapowania. Poza początkowym ręcznym ustawieniem i rzutowaniem wartości, reszta funkcji się nie zmienia.

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

Mapowania niestandardowe

Jeśli nazwy kolumn zapytań nie pasują do twoich klas, możesz ustawić odwzorowania dla typów. W tym przykładzie pokazano mapowanie przy użyciu System.Data.Linq.Mapping.ColumnAttribute a także niestandardowe mapowanie.

Mapowania należy skonfigurować tylko raz dla każdego typu, więc ustaw je przy uruchamianiu aplikacji lub w innym miejscu, aby były inicjowane tylko raz.

Przyjmując ponownie to samo zapytanie, co w przykładzie Jeden do wielu, a klasy zajęły się lepszymi nazwami:

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

Zauważ, że Book nie polega na ColumnAttribute ale musielibyśmy zachować instrukcję if

Teraz umieść ten kod mapowania gdzieś w aplikacji, gdzie jest wykonywany tylko raz:

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

Następnie zapytanie jest wykonywane przy użyciu dowolnego z poprzednich przykładów Query<> .

W tej odpowiedzi pokazano prostszy sposób dodawania mapowań.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow