Declaration of VAR

and some other stuff

Обращение к БД без LINQ

2014-10-26 13:28:28 +0300

2014-10-26 13:28:28 +0300 | Comments

История пошла в обратную сторону, и теперь проще найти, как выполнять запросы к БД с помощью LINQ, чем как это сделать старым добрым SQL. Я записал для себя, как выполнить обычный запрос на SELECT данных и также как вызвать хранимую процедуру.

ОСWindows 7 SP1 x64
языкC#
библиотеки.NET Framework 4
среда разработкиVisual Studio
база данныхMicrosoft SQL Server

Пара слов

LINQ очень удобен, с ним приятно работать, будь то LINQ to SQL или Entity Framework. Работает автодополнение с IntelliSense, всё видно прямо во время написания, не нужно смотреть в схему БД. Короче, все ништяки ORM.

Но есть вопрос производительности - это и не скрывается - что работа с БД через LINQ проигрывает в скорости обычным запросам SQL. У меня появилась такая задача: сделать выборку данных (id записи и одно поле), затем выполнить поиск по этому полю в другой базе и результат записать в ещё одно место. Записей в исходной таблице больше 50 000. Так вот, через LINQ это всё выполнялось довольно долго, примерно 2000 записей за 10 минут. Я решил проверить, как это будет на чистом SQL, и результат оказался что-то около 3000 записей в минуту. То есть, если свести в таблицу с равными значениями, то:

Язык Время Записей обработано
LINQ10 минут2 000
SQL30 000


Вот тут-то я выпилил всю эту линкушную ваниль из проекта.

Выполнение простого запроса

Обычный SELECT к таблице.

// сюда будем складывать полученные данные
Dictionary<long, string> infa = new Dictionary<long, string>();

// объявили соединение
using (SqlConnection sqlConn = new SqlConnection("Data Source=someserver;Initial Catalog=somecatalog;Integrated Security=True"))
// объявили команду (SQL-запрос)
using (SqlCommand cmd = new SqlCommand("SELECT id, name FROM sometable;", sqlConn))
{
    // открыли соединение
    sqlConn.Open();
    // выполнили команду, получили результат
    SqlDataReader reader = cmd.ExecuteReader();
    while (reader.Read()) // перебираем результат
    {
        // и сохраняем его в наш словарь
        infa.Add((long)reader[0], (string)reader[1]);
    }
}

Выхов хранимой процедуры с параметрами

Нужно вызвать хранимую процедуру с двумя входными параметрами.

// объявили соединение
using (SqlConnection sqlConn = new SqlConnection("Data Source=someserver;Initial Catalog=somecatalog;Integrated Security=True"))
// объявили команду (SQL-запрос)
using (SqlCommand cmd = new SqlCommand("searchInfoByName", sqlConn))
{
    // вот это важный момент, если его не указать, то процедура не будет выполняться, выдавая ошибку про отсутствующие параметры
    cmd.CommandType = CommandType.StoredProcedure;
    // у нас 50 000 записей, а стандартный таймаут 30 секунд, нам некогда по столько ждать
    cmd.CommandTimeout = 5;
    // открыли соединение
    sqlConn.Open();

    // для каждой записи словаря, собранного ранее, выполнить хранимую процедуру
    foreach (var rec in infa)
    {
        try // выполнять будем с перехватом исключений
        {
            // добавление параметров к процедуре
            cmd.Parameters.Add(new SqlParameter("@name", rec.Value));
            cmd.Parameters.Add(new SqlParameter("@id", rec.Key));
            // данные получать нам не нужно, только вызвать процедуру
            cmd.ExecuteNonQuery();
        }
        catch (Exception ex) 
        {
            // тут какая-нибудь запись в журнал ошибок
        }

        // очищаем параметры для следующей итерации
        cmd.Parameters.Clear();
    }
}

Ещё пара слов

Конечно, смотреть всегда надо исходя из ситуации. Один только этот случай не означает, что никогда и ни за что не надо использовать LINQ. Просто имейте в виду, что когда нужна производительность, то лучше не обвешивать работу с БД дополнительными фреймворками.