Поиск…


Синтаксис

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

параметры

параметр подробности
спп Соединение с базой данных, которое должно быть открыто.
SQL Команда для выполнения.
типы Массив типов в наборе записей.
карта Func<> который обрабатывает построение результата возврата.
пары Объект для извлечения параметров из.
сделка Транзакция, в которой этот запрос является частью, если таковой имеется.
буферном Будь или нет буферизация чтения результатов запроса. Это необязательный параметр с по умолчанию значением true. Когда буферизировано верно, результаты буферизуются в List<T> а затем возвращаются как IEnumerable<T> который безопасен для множественного перечисления. Если буферизация ложна, соединение sql будет открыто до тех пор, пока вы не закончите чтение, что позволит вам обрабатывать одну строку во времени в памяти. Множественные перечисления вызовут дополнительные подключения к базе данных. Хотя буферизованное ложное значение очень эффективно для сокращения использования памяти, если вы поддерживаете только очень мелкие фрагменты возвращенных записей, он имеет значительные накладные расходы по сравнению с желательным материализацией набора результатов. Наконец, если у вас есть многочисленные параллельные небуферизованные соединения sql, вам нужно рассмотреть головоломку пула соединений, вызывающую блокировку запросов до тех пор, пока соединения не станут доступными.
разделить на Поле должно разделить и прочитать второй объект из (default: id). Это может быть список с разделителями-запятыми, если в записи содержится более 1 типа.
CommandTimeout Количество секунд до истечения времени ожидания выполнения команды.
CommandType Это хранимая процедура или пакет?

Простое отображение нескольких таблиц

Предположим, у нас есть запрос оставшихся всадников, которым необходимо заполнить класс Person.

название Родившийся Резиденция
Даниэль Деннетт 1942 Соединенные Штаты Америки
Сэм Харрис 1967 Соединенные Штаты Америки
Ричард Докинз 1941 Объединенное Королевство
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; }
}

Мы можем заполнить класс person, а также свойство Residence экземпляром страны, используя перегруженный Query<> который принимает Func<> который может использоваться для компоновки возвращаемого экземпляра. Func<> может принимать до 7 типов ввода с окончательным общим аргументом, всегда являющимся типом возврата.

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

Обратите внимание на использование splitOn: "Residence" который является 1-м столбцом следующего типа класса, который будет заполнен (в данном случае Country ). Dapper автоматически ищет столбец с именем Id для разделения, но если он не найдет его, а splitOn не будет предоставлен, System.ArgumentException будет splitOn полезным сообщением. Поэтому, хотя это необязательно, вам обычно нужно предоставить значение splitOn .

Отображение «один ко многим»

Давайте рассмотрим более сложный пример, содержащий отношения «один ко многим». Теперь наш запрос будет содержать несколько строк, содержащих повторяющиеся данные, и нам нужно будет справиться с этим. Мы делаем это с поиском в закрытии.

Запрос слегка меняется, как и классы примеров.

Я бы название Родившийся CountryId Название страны BookID BookName
1 Даниэль Деннетт 1942 1 Соединенные Штаты Америки 1 Мозговые штурмы
1 Даниэль Деннетт 1942 1 Соединенные Штаты Америки 2 Колено
2 Сэм Харрис 1967 1 Соединенные Штаты Америки 3 Моральный пейзаж
2 Сэм Харрис 1967 1 Соединенные Штаты Америки 4 Пробуждение: руководство к духовности без религии
3 Ричард Докинз 1941 2 Объединенное Королевство 5 Магия реальности: как мы знаем, что действительно верно
3 Ричард Докинз 1941 2 Объединенное Королевство 6 Аппетит к удивлению: создание учёного
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; }
}

Словарь, remainingHorsemen всадниках, будет заполнен полностью материализованными экземплярами объектов человека. Для каждой строки результата запроса передаются сопоставленные значения экземпляров типов, определенных в аргументах лямбда, и вам решать, как справиться с этим.

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

Обратите внимание, как аргумент splitOn представляет собой список с разделителями-запятыми из первых столбцов следующего типа.

Отображение более 7 типов

Иногда количество отображаемых типов превышает 7, предоставленное Func <>, которое выполняет конструкцию.

Вместо использования Query<> с входами аргумента generic type мы предоставим типы для отображения в виде массива, а затем функцию отображения. Помимо первоначальной ручной настройки и литья значений, остальная часть функции не изменяется.

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

Пользовательские сопоставления

Если имена столбцов запроса не соответствуют вашим классам, вы можете настроить сопоставления для типов. Этот пример демонстрирует сопоставление с использованием System.Data.Linq.Mapping.ColumnAttribute а также настраиваемое сопоставление.

Отображения нужно настраивать только один раз для каждого типа, чтобы они были установлены при запуске приложения или где-то еще, что они только инициализируются один раз.

Предположив тот же запрос, что и пример «один-ко-многим», и классы, реорганизованные для более удобных имен:

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

Обратите внимание, что Book не полагается на ColumnAttribute но нам нужно будет поддерживать оператор if

Теперь разместите этот код сопоставления где-нибудь в своем приложении, где он выполняется только один раз:

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

Затем запрос выполняется с использованием любого из предыдущих примеров Query<> .

В этом ответе показан более простой способ добавления отображений.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow