Приложение выполнено на каркасе Windows Forms. Для просмотра списка баз данных SQL сервера LocalDB и их содержимого используются элементы управления DataGridView. При загрузке приложения в первом DataGridView dgvDatabase выводится список баз данных. Щелкая по строкам баз данных, во втором DataGridView dgvTables отображается список таблиц соответствующей базы. Выбирая строки таблиц, в третьем DataGridView dgvContentTable раскрывается выбранная таблица с данными столбцов. Ошибки обработки входных данных отслеживаются в методе события DataError.
Приложение работает на драйвере поставщика данных пространства имен Microsoft.Data.SqlClient технологии доступа к данным ADO.NET, библиотеки добавляются в проект посредством сервиса загрузки пакетов NuGet. Результаты SQL запросов возвращаются в виде объекта DataTable. Таблицы, в свою очередь, являются источником данных для элементов управления DataGridView.
Примечание. Для SQL клиента рекомендуется использовать пространство имен Microsoft.Data.SqlClient, но не System.Data.SqlClient, так как более расширенная поддержка и новые функции (в том числе поддержка UTF-8) будут внедряться только в Microsoft.Data.SqlClient. Ссылка на информацию о SQL клиентах в статье Introducing the new Microsoft.Data.SqlClient
DataGridView предназначен для отображения данных в виде таблицы. Источник данных указывается для свойства DataGridView.DataSource. Источники данных могут быть различных типов. В документации в качестве источников указаны типы реализующие следующие интерфейсы:
IList, включая одномерные массивы;
IListSource, например классы DataTable и DataSet;
IBindingList, например класс BindingList;
IBindingListView, например класс BindingSource.
В описываемом приложении источником данных служат объекты DataTable, представляющие одну таблицу в памяти.
Для инкапсуляции подключения и формирования запросов к серверу баз данных создан статический класс ConnectDB с одним закрытым свойством и одним открытым методом выполнения SQL запросов. Строка подключения создается объектом специализированного класса SqlConnectionStringBuilder, предназначенного для построения синтактически правильных строк подключения. Для работы со списком баз данных сервера нет необходимости определять свойство SqlConnectionStringBuilder.InitialCatalog. Для подключения к LocalDB используется режим проверки текущей учетной записи Windows.
Программный код класса формирующий подключение и запросы к серверу LocalDB:
internal class ConnectDB
{
// Формирование строки подключения к локальному серверу баз данных.
private static string ConnectionString
{
get
{
var sb = new SqlConnectionStringBuilder
{
// Путь подключения к локальному серверу,
// Экземпляры локальных серверов могут отличаться именами.
DataSource = @"(LocalDB)MSSQLLocalDB",
// Проверка подлинности Windows.
IntegratedSecurity = true,
Pooling = true
};
return sb.ConnectionString;
}
}
// Метод выполнения запросов к SQL серверу.
public static DataTable SqlQuery(string queryString)
{
string connectionString = ConnectDB.ConnectionString;
using SqlConnection connection = new(connectionString);
using SqlCommand command = new(queryString, connection);
connection.Open();
// Результаты возвращаются в виде таблицы.
DataTable result = new();
using SqlDataReader reader = command.ExecuteReader();
result.Load(reader);
return result;
}
}
Доступ к основным параметрам баз данных SQL сервера предоставляет системное представление SYS.DATABASES в виде таблицы со множеством столбцов показателей. Для каждой базы данных отводится одна строка системной таблицы.
Инициализация списка баз данных локального SQL сервера происходит в методе Init(), который вызывается в конструкторе главной формы приложения. Для каждой базы данных запрос ограничивается несколькими параметрами: имя, идентификатор, состояние базы (ONLINE, OFFLINE и др.), уровень совместимости (версия SQL сервера), доступ пользователя (MULTI_USER, SINGLE_USER, RESTRICTED_USER) и дата создания. Для удобства параметры могут быть переименованы, при этом имена включающие пробелы должны быть заключены в квадратные скобки.
Возврат запроса в виде объекта DataTable присваивается свойству DataGridView.DataSource. В результате работы инициирующего метода поле DataGridView заполняется списком баз данных сервера LocalDB, визуализируя информацию в виде таблицы.
private void Init()
{
// Имя параметра состоящее из двух слов должны быть заключены в квадратные скобки,
// оно будет восприниматься как неразрывное целое.
string queryString = "SELECT name, database_id as Идентификатор, state_desc as Состояние, compatibility_level as [Версия SQL Server], user_access_desc as [Доступ пользователя], create_date as [Дата создания] FROM sys.databases;";
// Запрошенные данные выдаются в виде таблицы.
DataTable dataTable = ConnectDB.SqlQuery(queryString);
// Полученная таблица задается в качестве источника данных DataGridView.
// Данные выводятся в верхний DataGridView, предназначенный для
// отображения информации о базах данных SQL сервера.
dgvDatabase.DataSource = dataTable;
}
При выделении какой-либо ячейки в первом DataGridView отображения баз данных, во втором DataGridView выводится таблица с данными всех таблиц выбранной базы данных. Информацию о таблицах базы данных можно получить различными способами, например: метаданные из системного представления INFORMATION_SCHEMA.TABLES и sys.tables. Перед запросом информации о таблицах выбранную базу указываем инструкцией "USE Database_Name", тогда выдаются сведения о таблицах только указанной базы данных.
В качестве метода исполняющего процедуру запроса списка таблиц для следующего элемента DataGridView, в приложении используется событие получения фокуса RowEnter строкой объекта DataGridView dgvDatabase . Имя выбранной базы данных сохраняется в свойстве dgvDatabase.Tag, это свойство имеет тип object и поэтому может хранить объекты любого типа. Сохранение имени выбранной базы необходимо для отображения столбцов выбранной таблицы в последнем элементе DataGridView.
private void DgvDatabase_RowEnter(object sender, DataGridViewCellEventArgs e)
{
// Запоминаем выбранную базу данных.
dgvDatabase.Tag = dgvDatabase.Rows[e.RowIndex].Cells["name"].Value;
string queryString = "USE [" + dgvDatabase.Tag + "]; SELECT * FROM INFORMATION_SCHEMA.TABLES";
// Запрос выдающий аналогичные данные.
// string queryString = "SELECT * FROM [" + dgvDatabase.Tag + "].INFORMATION_SCHEMA.TABLES";
// Дополнительная, более полная, информация о таблицах выбранной базы данных.
// string queryString = "USE [" + dgvDatabase.Tag + "]; SELECT * FROM sys.tables";
DataTable dataTable = ConnectDB.SqlQuery(queryString);
// Передача полученных данных следующему DataGridView
dgvTables.DataSource = dataTable;
}
Следующий этап от общего к частному - отображение подробностей выбранной таблицы. Для этого в событии RowEnterDataGridView dgvTables выполняется запрос к серверу LocalDB, используя сохраненное имя текущей базы данных и индекс фокусной строки. Результат присваивается элементу DataGridView dgvContentTable в качестве источника данных.
private void DgvTables_RowEnter(object sender, DataGridViewCellEventArgs e)
{
// Используем сохраненное имя базы данных.
// Имя таблицы получаем от столбца TABLE_NAME выбранной строки.
string queryString = "USE [" + dgvDatabase.Tag + "]; SELECT * FROM " +
dgvTables.Rows[e.RowIndex].Cells["TABLE_NAME"].Value;
DataTable dataTable = ConnectDB.SqlQuery(queryString);
// Передача данных в DataGridView отображения столбцов
// выбранной строки.
dgvContentTable.DataSource = dataTable;
}
У класса DataGridView есть удобное событие DataGridView.DataError, в котором можно отследить возникновение ошибки во время анализа входной информации источника данных и управлять исключением вызванным этой ошибкой. По умолчанию, пустой метод события освобождает приложение от выбрасывания исключений связанных с обработкой источника данных.
Для упрощения один метод отслеживает события ошибок во всех объектах DataGridView, параметр sender служит для определения объекта, в котором произошла ошибка. Подробная текстовая информация о возникшей ошибке и координаты ячейки выводятся в диалоговое окно. Практический пример обработки ошибок можно посмотреть на работа DataGridView в паре с DataTable.
private void dgvDatabase_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
// --- Перехват ошибок ---
DataGridView? dgv = sender as DataGridView;
// Формирование текстовой информации об ошибке и
// координате невалидной ячейки.
string error = "Ошибка в ";
// Определение объекта DataGridView в котором произошла ошибка.
string select = dgv switch
{
_ when dgvDatabase == dgv => "dgvDatabase!",
_ when dgvTables == dgv => "dgvTables!",
_ when dgvContentTable == dgv => "dgvContentTable!",
_ => error = "Неизвестная ошибка!"
};
string coordinateError = "
" + "row=" + e.RowIndex + ";col=" + e.ColumnIndex + ";";
MessageBox.Show(error + select + coordinateError + "
" +
e.Exception.Message, "Внимание!",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}