[.NET, C#, Big Data] Новый .NET клиент для Snowflake
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Всем привет!
В этой статье я расскажу, как я сделал свой собственный .NET клиент для работы со Snowflake, чем он лучше официальных библиотек, как устроен и как им пользоваться.
Для работы со Snowflake в .NET приложении до недавнего времени у разработчиков было всего два варианта: ODBC драйвер и Snowflake .NET Connector. Я попробовал оба, но ни тот ни другой мне не понравились.
Мотивация
Процесс установки и настройки ODBC драйвера для Snowflake на Windows, мягко говоря, не самый простой и понятный. Правда, это относится ко всем ODBC драйверам, не только к Snowflake. У меня ушла пара часов чтобы наконец заставить его работать. К сожалению, его нужно установить и настроить на всех машинах, где запускается ваше приложение — т.е. это дополнительная зависимость. Из интереса я провел небольшое сравнение производительности со Snowflake Connector, и оказалось, что ODBC драйвер заметно медленней.
.NET Snowflake Connector (Snowlfake.Data) — это официальная библиотека с открытым исходным кодом, распространяется в виде NuGet пакета. Одна из существенных проблем, которые у неё есть — в ней не реализован пул соединений (connection pooling). Т.е. каждый раз, когда вы создаете новое соединение с БД, инициализируется новое соединение. Если вы следуете best practice и официальной документации, то ваш типичный код для чтения данных из Snowflake при использовании Snowflake.Data выглядит примерно так:
using (var conn = new SnowflakeDbConnection())
{
conn.ConnectionString = connectionString;
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT * FROM table;";
var reader = cmd.ExecuteReader();
while(reader.Read())
{
Console.WriteLine(reader.GetString(0));
}
conn.Close();
}
Без пула соединений в этом примере произойдет три реквеста к Snowflake: создание новой сессии, выполнение SQL запроса и закрытие сессии. Т.е. по 3 реквеста на каждый SQL запрос. Это не очень эффективно и негативно влияет на производительность.
Как вы могли заметить из примера выше, Snowflake.Data — это ADO.NET коннектор. Разработчики официальной библиотеки почему-то решили реализовать ADO.NET интерфейсы, несмотря на то что Snowflake — это не традиционная база данных. Это скорее нативно-облачная база данных с REST API. Да, внутри этот коннектор использует REST API, но при этом реализует интерфейсы ADO.NET, прикидываясь классической БД для разработчика.
Реализация ADO.NET интерфейсов поверх REST API добавляет сложности в разработку, потому что это совершенно разные интерфейсы. Разрабатывая такой коннектор придется иметь дело с ограничениями, которые накладываются интерфейсами ADO.NET. Например, вам нужно реализовать передачу параметра в REST API запросе, но в ADO.NET попросту нет подходящей опции или фичи (или вам придется использовать существующие фичи не совсем естественным образом). С другой стороны, многие фичи ADO.NET просто не могут быть реализованы, потому что в REST API нет соответствующих фич. Вот почему в коде коннектора там много методов, которые просто выбрасывают NotImplementedException.
На мой взгляд более естественным выбором была бы реализация клиента для REST API. Немного погуглив, я не нашел ничего похожего и решил написать свой клиент: Snowflake.Client.
Создание сессии
Новая сессия в Snowflake.Client создается очень просто: создайте новый клиент, и новая сессия инициализируется автоматически:
// Creates new client and initializes new session
var snowflakeClient = new SnowflakeClient("user", "password", "account", "region");
На данный момент поддерживается только базовая аутентификация с помощью пары логин/пароль. Для удобства есть несколько вариантов конструктора SnowflakeClient, в них можно передать дополнительные параметры. Например, можно передать свои настройки для маппинга. Созданная сессия будет использоваться для всех последующих вызовов, сделанных с помощью этого клиента. Информацию о текущей сессии можно посмотреть в свойстве Session у клиента.
Выполнение запросов
В Snowflake.Client есть несколько методов для выполнения SQL запросов в Snowflake. Вот несколько примеров:
// Executes query and maps response data to your class
var employees = snowflakeClient.Query<Employee>("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;");
// Executes query and returns value of first cell as string result
string useRoleResult = snowflakeClient.ExecuteScalar("USE ROLE ACCOUNTADMIN;");
// Executes query and returns affected rows count
int affectedRows = snowflakeClient.Execute("INSERT INTO EMPLOYEES Title VALUES (?);", "Dev");
Как вы могли заметить, синтаксис очень похож на Dapper. Так что если вы работали с Dapper, то вы уже знаете, как пользоваться Snowflake.Client.
Параметризация запросов
Для безопасного выполнения SQL запросов, в составе которых есть данные, вводимые пользователем, используются параметры. Давайте посмотрим, как можно сделать параметризированный запрос с помощью Snowflake.Data:
using (IDbConnection conn = new SnowflakeDbConnection())
{
conn.ConnectionString = connectionString;
conn.Open();
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = "INSERT INTO table VALUES (?)";
var p1 = cmd.CreateParameter();
p1.ParameterName = "1";
p1.Value = 10;
p1.DbType = DbType.Int32;
cmd.Parameters.Add(p1);
var count = cmd.ExecuteNonQuery();
conn.Close();
}
Да, оригинальный интерфейс ADO.NET очень громоздкий. Во многих реализациях есть методы-обертки для сокращения кода, например AddWithValue(). К сожалению, в реализации Snowflake ничего такого нет, и код очень быстро раздувается.
Snowflake REST API поддерживает два типа параметров:
- позиционные — знак вопроса (?)
- именованные — имя параметра с двоеточием (:name)
Snowflake.Client поддерживает оба типа. Позиционные параметры используются со встроенными типами (string, int, DateTime и др.):
// Positional binding with question marks:
var a = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE TITLE = ?", "Programmer");
var b = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE ID = ?", 3);
var c = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE ID IN (?,?)", new int[] { 1, 2 });
Именованные параметры используются с классами, в том числе анонимными:
// Named binding with colons:
var d = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE TITLE = :Title", new Employee() { Title = "Programmer" });
var e = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE TITLE = :Title", new { Title = "Programmer" });
Маппинг
Snowflake REST API возвращает данные запроса в виде двух массивов: колонки и строки. Примерно так (сокращено для читабельности):
"data":{
"rowtype":[
{
"name":"created_on",
"type":"timestamp_ltz"
},
{
"name":"name",
"type":"text"
}
],
"rowset":[ "1579896098.349", "Eleanor" ]
}
Такой формат может быть полезен в некоторых ситуациях, но обычно мы хотим иметь данные в виде объектов. Для задач такого рода обычно используется Activator.CreateInstance() и рефлексия. Однако, имея на руках JSON строку гораздо проще использовать JSON сериализатор. Осталось только преобразовать исходную JSON строку в другую, которая будет представлять возвращённый объект. Для примера выше она будет выглядеть вот так:
{ “created_on”: “1579896098.349”, “name”: “Eleanor” }.
Пример запроса с маппингом:
var employees = snowflakeClient.Query<Employee>("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;");
Естественно, при этом Snowflake.Client делает конвертацию типов Snowflake в типы .NET. Чтобы изменить поведение маппера (т.е. JSON-сериализатора) можно передать кастомные опции (JsonSerializerOptions) в конструктор клиента.
Другие фичи
Как я упоминал выше — сырой формат данных (колонки и строки) может быть полезен в некоторых ситуациях. Так что я добавил метод, который возвращает данные в нетронутом виде: QueryRaw().
Snowflake.Client в отличии от Snowflake.Data, поддерживает флаг describeOnly для запросов. Если он установлен в true, то в ответе будут только колонки, без самих данных. Это может быть полезно, если вы хотите получить информацию о колонках: тип данных, точность, размер и другие свойства.
Заключение
Работы ещё много, но уже сейчас Snowflake.Client готов для базового использования. Надеюсь, в будущем он сможет составить конкуренцию официальному коннектору.
P.S. На гитхабе создан специальный тред для идей и фидбека. Пулл реквесты и баг-репорты также приветствуются!
Ссылки
===========
Источник:
habr.com
===========
Похожие новости:
- [.NET, C#, Microsoft Azure, Игры и игровые приставки] Elite: Dangerous и CosmosDB
- [.NET, C#] Дерево синтаксиса и альтернатива LINQ при взаимодействии с базами данных SQL (перевод)
- [.NET, C#, Конференции, Микросервисы] .NET Community Meetup 29/10
- [Big Data, Хранение данных, Хранилища данных, Накопители] Технологии магнитной записи HDD: просто о сложном
- [Информационная безопасность, Big Data] Участники рынка больших данных в РФ хотят интегрировать данные частных компаний и ГИС
- [Занимательные задачки, Big Data, Визуализация данных] Рейтинг знаков зодиака среди Великих людей мира
- [Apache, Big Data, Hadoop] Spark schemaEvolution на практике
- [Unity, C#, Разработка игр] Как мы переосмыслили работу со сценами в Unity
- [Big Data, Data Engineering] Магия Ensemble Learning (перевод)
- [.NET] Interprocess communication с использованием GRPC
Теги для поиска: #_.net, #_c#, #_big_data, #_snowflake, #_snowflakedb, #_.net, #_c#, #_connector, #_.net, #_c#, #_big_data
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:45
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Всем привет! В этой статье я расскажу, как я сделал свой собственный .NET клиент для работы со Snowflake, чем он лучше официальных библиотек, как устроен и как им пользоваться. Для работы со Snowflake в .NET приложении до недавнего времени у разработчиков было всего два варианта: ODBC драйвер и Snowflake .NET Connector. Я попробовал оба, но ни тот ни другой мне не понравились. Мотивация Процесс установки и настройки ODBC драйвера для Snowflake на Windows, мягко говоря, не самый простой и понятный. Правда, это относится ко всем ODBC драйверам, не только к Snowflake. У меня ушла пара часов чтобы наконец заставить его работать. К сожалению, его нужно установить и настроить на всех машинах, где запускается ваше приложение — т.е. это дополнительная зависимость. Из интереса я провел небольшое сравнение производительности со Snowflake Connector, и оказалось, что ODBC драйвер заметно медленней. .NET Snowflake Connector (Snowlfake.Data) — это официальная библиотека с открытым исходным кодом, распространяется в виде NuGet пакета. Одна из существенных проблем, которые у неё есть — в ней не реализован пул соединений (connection pooling). Т.е. каждый раз, когда вы создаете новое соединение с БД, инициализируется новое соединение. Если вы следуете best practice и официальной документации, то ваш типичный код для чтения данных из Snowflake при использовании Snowflake.Data выглядит примерно так: using (var conn = new SnowflakeDbConnection())
{ conn.ConnectionString = connectionString; conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = "SELECT * FROM table;"; var reader = cmd.ExecuteReader(); while(reader.Read()) { Console.WriteLine(reader.GetString(0)); } conn.Close(); } Без пула соединений в этом примере произойдет три реквеста к Snowflake: создание новой сессии, выполнение SQL запроса и закрытие сессии. Т.е. по 3 реквеста на каждый SQL запрос. Это не очень эффективно и негативно влияет на производительность. Как вы могли заметить из примера выше, Snowflake.Data — это ADO.NET коннектор. Разработчики официальной библиотеки почему-то решили реализовать ADO.NET интерфейсы, несмотря на то что Snowflake — это не традиционная база данных. Это скорее нативно-облачная база данных с REST API. Да, внутри этот коннектор использует REST API, но при этом реализует интерфейсы ADO.NET, прикидываясь классической БД для разработчика. Реализация ADO.NET интерфейсов поверх REST API добавляет сложности в разработку, потому что это совершенно разные интерфейсы. Разрабатывая такой коннектор придется иметь дело с ограничениями, которые накладываются интерфейсами ADO.NET. Например, вам нужно реализовать передачу параметра в REST API запросе, но в ADO.NET попросту нет подходящей опции или фичи (или вам придется использовать существующие фичи не совсем естественным образом). С другой стороны, многие фичи ADO.NET просто не могут быть реализованы, потому что в REST API нет соответствующих фич. Вот почему в коде коннектора там много методов, которые просто выбрасывают NotImplementedException. На мой взгляд более естественным выбором была бы реализация клиента для REST API. Немного погуглив, я не нашел ничего похожего и решил написать свой клиент: Snowflake.Client. Создание сессии Новая сессия в Snowflake.Client создается очень просто: создайте новый клиент, и новая сессия инициализируется автоматически: // Creates new client and initializes new session
var snowflakeClient = new SnowflakeClient("user", "password", "account", "region"); На данный момент поддерживается только базовая аутентификация с помощью пары логин/пароль. Для удобства есть несколько вариантов конструктора SnowflakeClient, в них можно передать дополнительные параметры. Например, можно передать свои настройки для маппинга. Созданная сессия будет использоваться для всех последующих вызовов, сделанных с помощью этого клиента. Информацию о текущей сессии можно посмотреть в свойстве Session у клиента. Выполнение запросов В Snowflake.Client есть несколько методов для выполнения SQL запросов в Snowflake. Вот несколько примеров: // Executes query and maps response data to your class
var employees = snowflakeClient.Query<Employee>("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;"); // Executes query and returns value of first cell as string result string useRoleResult = snowflakeClient.ExecuteScalar("USE ROLE ACCOUNTADMIN;"); // Executes query and returns affected rows count int affectedRows = snowflakeClient.Execute("INSERT INTO EMPLOYEES Title VALUES (?);", "Dev"); Как вы могли заметить, синтаксис очень похож на Dapper. Так что если вы работали с Dapper, то вы уже знаете, как пользоваться Snowflake.Client. Параметризация запросов Для безопасного выполнения SQL запросов, в составе которых есть данные, вводимые пользователем, используются параметры. Давайте посмотрим, как можно сделать параметризированный запрос с помощью Snowflake.Data: using (IDbConnection conn = new SnowflakeDbConnection())
{ conn.ConnectionString = connectionString; conn.Open(); IDbCommand cmd = conn.CreateCommand(); cmd.CommandText = "INSERT INTO table VALUES (?)"; var p1 = cmd.CreateParameter(); p1.ParameterName = "1"; p1.Value = 10; p1.DbType = DbType.Int32; cmd.Parameters.Add(p1); var count = cmd.ExecuteNonQuery(); conn.Close(); } Да, оригинальный интерфейс ADO.NET очень громоздкий. Во многих реализациях есть методы-обертки для сокращения кода, например AddWithValue(). К сожалению, в реализации Snowflake ничего такого нет, и код очень быстро раздувается. Snowflake REST API поддерживает два типа параметров:
Snowflake.Client поддерживает оба типа. Позиционные параметры используются со встроенными типами (string, int, DateTime и др.): // Positional binding with question marks:
var a = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE TITLE = ?", "Programmer"); var b = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE ID = ?", 3); var c = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE ID IN (?,?)", new int[] { 1, 2 }); Именованные параметры используются с классами, в том числе анонимными: // Named binding with colons:
var d = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE TITLE = :Title", new Employee() { Title = "Programmer" }); var e = snowflakeClient.QueryScalar("SELECT COUNT(*) FROM EMPLOYEES WHERE TITLE = :Title", new { Title = "Programmer" }); Маппинг Snowflake REST API возвращает данные запроса в виде двух массивов: колонки и строки. Примерно так (сокращено для читабельности): "data":{
"rowtype":[ { "name":"created_on", "type":"timestamp_ltz" }, { "name":"name", "type":"text" } ], "rowset":[ "1579896098.349", "Eleanor" ] } Такой формат может быть полезен в некоторых ситуациях, но обычно мы хотим иметь данные в виде объектов. Для задач такого рода обычно используется Activator.CreateInstance() и рефлексия. Однако, имея на руках JSON строку гораздо проще использовать JSON сериализатор. Осталось только преобразовать исходную JSON строку в другую, которая будет представлять возвращённый объект. Для примера выше она будет выглядеть вот так: { “created_on”: “1579896098.349”, “name”: “Eleanor” }. Пример запроса с маппингом: var employees = snowflakeClient.Query<Employee>("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;");
Естественно, при этом Snowflake.Client делает конвертацию типов Snowflake в типы .NET. Чтобы изменить поведение маппера (т.е. JSON-сериализатора) можно передать кастомные опции (JsonSerializerOptions) в конструктор клиента. Другие фичи Как я упоминал выше — сырой формат данных (колонки и строки) может быть полезен в некоторых ситуациях. Так что я добавил метод, который возвращает данные в нетронутом виде: QueryRaw(). Snowflake.Client в отличии от Snowflake.Data, поддерживает флаг describeOnly для запросов. Если он установлен в true, то в ответе будут только колонки, без самих данных. Это может быть полезно, если вы хотите получить информацию о колонках: тип данных, точность, размер и другие свойства. Заключение Работы ещё много, но уже сейчас Snowflake.Client готов для базового использования. Надеюсь, в будущем он сможет составить конкуренцию официальному коннектору. P.S. На гитхабе создан специальный тред для идей и фидбека. Пулл реквесты и баг-репорты также приветствуются! Ссылки =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:45
Часовой пояс: UTC + 5