Все необходимые для выполнения данной работы программы можно найти в прилагаемом каталоге.
Наиболее современной технологией разработки приложений, управляющих данными, является построение многоуровневых систем:
Доступ к данным обеспечивает обобщенный поставщик данных, который включает в себя следующие классы:
DbConnection - используется для установки соединения с источником данныхDbCommand - используется для выполнения SQL-команд и хранимых процедурDbDataReader - модуль чтения, который предоставляет быстрый последовательный
доступ к данным только для чтения. Он сам автоматически не управляет открытием
и закрытием соединения и это нужно делать вручную, открывая как можно позже
и закрывая как можно раньшеDbDataAdapter - выполняет две задачи:DataSet (автономная коллекция таблиц и отношений)
информацией, извлеченной из источника данныхDataSet,
к источнику данныхЭтот обобщенный поставщик напрямую не применяется, а наследуется специализированными
поставщиками, имена которых имеют префиксы Odbc, OleDb, Sql, Oracle.
Мы будем использовать набор классов OleDb для файловой БД Northwind.mdb. Нужно четко понимать, что поставщики ADO.NET отображают
реляционную модель данных хранилища в объектно-ориентированную модель приложения. Это
значит, что данные таблицы или множества таблиц после их считывания приложением
представляются наборами взаимосвязанных объектов соответствующих типов и дальнейшая
работа проводится уже с этими объектами.
Объект DbCommand может выполнять три типа команд, определяемых перечислением CommandType:
Text - команда будет выполнять прямой SQL-оператор, который мы
укажем в свойстве DbCommand.CommandTextStoredProcedure - команда будет выполнять созданную нами ранее процедуру,
хранимую в источнике данных. Свойство DbCommand.CommandText представляет
имя хранимой процедурыTableDirect - команда извлечет все записи указанной в свойстве DbCommand.CommandText таблицы
подключенной БД (не работает с поставщиком SQL Server)Для выполнения созданного и настроенного объекта DbCommand он имеет три
команды:
ExecuteReader() - выполняет запрос SELECT и возвращает объект DbDataReader,
который является оболочкой однонаправленного курсора, доступного только для
чтенияExecuteScalar() - выполняет запрос SELECT и возвращает значение
первого поля первой строки из набора строк, сгенерированного командой. Этот
метод обычно применяется при исполнении агрегатной команды SELECT, использующей
функции наподобие COUNT() или SUM() для вычисления единственного значенияExecuteNonQuery() - для выполнения незапросной команды, которая
не требует выборки данных с помощью SQL-оператора SELECT. Применяется для
исполнения SQL-команд вставки, удаления или обновления записей. Возвращаемое
значение означает количество строк, обработанное командойМодуль чтения DbDataReader способен загрузить в себя данные из нескольких
таблиц одной БД, т.е. получить несколько наборов результатов. Доступ к считанным
наборам результатов можно получить с помощью методов DbDataReader. Вот
некоторые из них:
| Метод | Описание |
|---|---|
bool Read() |
Перемещает курсор строки результирующего набора на следующую строку.
Этот метод должен быть вызван, также, перед чтением первой строки данных,
т.к. после наполнения DbDataReader данными курсор устанавливается перед
первой строкой. Метод возвращает true, если есть куда переводить, при применении
к последней строке он вернет false |
object GetValue(int |
Возвращает значение поля текущей строки с указанным индексом столбца. Тип возвращенного значения заменяется типом .NET, наиболее подходящим типу столбца в хранилище |
int GetValues(object[ ] values) |
Заполняет массив values полями текущей записи. Количество полей определяется
размером массива, который можно определить по свойству |
int GetInt32(int |
Эти методы возвращают значение поля с указанным индексом в текущей строке (нумерайия полей начинается с нуля). Тип поля определяет имя метода |
char GetChar(int |
|
bool GetBoolean(int |
|
System.DateTime GetDateTime(int |
|
bool NextResult() |
Переводит курсор перед первой строкой следующего набора результатов,
если было прочитано несколько наборов составной SQL-командой SELECT |
void Close() |
Закрывает модуль чтения |
Когда данных для чтения много или они требуют длительной обработки, рационально сначала считать все данные, затем закрыть соединение и после этого обрабатывать данные в отключенном режиме.
Создайте командой File/New/Project новое
решение с именем ADO и
проект WinForms1 для первого упражнения
Поместите на форму
из панели Toolbox нужные компоненты и
настройте их в соответствии с таблицей свойств| Компонент | Свойство | Значение |
|---|---|---|
Form |
Text |
Упражнение 1 |
DataGridView |
(Name) |
myDataGrid |
Dock |
Top |
|
AutoSizeColumnsMode |
Fill |
|
Button |
(Name) |
btnPopulate |
Text |
Заполнить |
|
AutoSize |
true |
|
Button |
(Name) |
btnDataBind |
Text |
Связать |
|
AutoSize |
true |
|
Label |
(Name) |
lblRegCount |
Text |
Количество записей таблицы: |
В панели Solution
Explorer создайте папку Data для
корневого узла проекта командой Add/New Folder, вызовите для
нее контекстное меню и скопируйте из прилагаемой папки Source файл Northwind.mdb командой Add/Existing Item. При появлении мастера Data Source Configuration
Wizard отмените его кнопкой Cancel
Выделите в Solution
Explorer файл Northwind.mdb и в панели Properties проверьте, что его
свойства имеют значение
Создайте обработчик
для кнопки btnPopulate и btnDataBind
Внесите изменения в
файл Form1.cs, выделенные в листингеusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Дополнительные пространства имен
using System.Data.OleDb;
using System.Data.Common;
using System.Collections;
namespace WinForms1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Отключаем кнопку 'Связать'
btnDataBind.Enabled = false;
// Отключить системную кнопку
this.MaximizeBox = false;
}
// Создаем объект массива списков
ArrayList dbRecordsList = new ArrayList();// Поле
// Строка соединения с абсолютным путем к БД, определяемым сборкой
String ConnectionString(String fileName)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileName.Trim() + ".mdb";
}
// Заполнение массива списков данными
private void btnPopulate_Click(object sender, EventArgs e)
{
// Создаем объект соединения
OleDbConnection objConnection =
new OleDbConnection(ConnectionString("Northwind"));
dbRecordsList.Clear();// Очищаем список (необязательно!)
// После выполнения тела инструкции using освободится подключение
using (objConnection)
{
// Создаем объект команды с последующими настройками
OleDbCommand objCommand = new OleDbCommand();
objCommand.CommandType = CommandType.Text;// Не обязательно, по умолчанию
objCommand.CommandText = "SELECT * FROM Customers";
objCommand.Connection = objConnection;
// Или сразу одним конструктором
//OleDbCommand objCommand = new OleDbCommand("SELECT * FROM Customers", objConnection);
objConnection.Open();// Открываем соединение
// Выполняем объект DbCommand одним из его методов
OleDbDataReader dataReader =
objCommand.ExecuteReader( // Заполнить набор данных
CommandBehavior.CloseConnection); // Отработать и сразу закрыть соединение
// Счетчик количества записей
int recCount = 0;
if (dataReader.HasRows)// Флаг поднят, если есть строки
{
// Перебрать все строки набора и добавить в коллекцию списка
foreach (DbDataRecord rec in dataReader)
{
dbRecordsList.Add(rec);
recCount++;// Не будем использовать, подсчитаем ниже командой
}
}
// Подсчитываем число записей в таблице
objCommand = new OleDbCommand("SELECT COUNT(*) FROM Customers", objConnection);
objConnection.Open();
recCount = (int)objCommand.ExecuteScalar();
objConnection.Close();
lblRegCount.Text += recCount.ToString();
}
// Переключаем доступность кнопок
btnPopulate.Enabled = false;
btnDataBind.Enabled = true;
}
private void btnDataBind_Click(object sender, EventArgs e)
{
// Связываем список с элементом отображения
myDataGrid.DataSource = dbRecordsList;
btnDataBind.Enabled = false;// Отключаем кнопку
}
}
}
Запустите приложение,
чтобы получить результат
DbDataReader, возвращаемый методом objCommand.ExecuteReader(), позволяет читать
данные в соответствии с исполняемой командой SELECT построчно в однонаправленном,
доступном только для чтения потоке. Применение DbDataReader - простейший путь
извлечения данных из хранилища, но ему недостает возможностей сортировки и
связывания автономного DataSet.
DbDataReader не позволяет выполнять обратный просмотр набора результатов
и редактировать их. Это лишь способ последовательного чтения в одном направлении,
использующий постоянное подключение к БД. В соответствии со значением перечисления CommandBehavior.CloseConnection соединение
с БД закрывается сразу же, как только объект команд закончит чтение
данных в DbDataReader. Но для подстраховки в
нашем приложении модуль чтения и подключение закрываются автоматически после
завершения инструкции using.
В отключенном
режиме загруженные данные находятся в объекте ArrayList и показываются
пользователю после связывания с DataGridView. Объект DataGridView,
в нашем случае, показывает данные в режиме только для чтения, поскольку 'знает',
что ArrayList не
является редактируемым.
Часто пользователь хочет просмотреть часть результатов, хранящихся в некоторой
таблице БД. Здесь также может пригодиться модуль чтения DbDataReader.
В данном упражнении мы разработаем форму, в которой пользователь выбирает из
раскрывающегося списка конкретного служащего и просматривает заказы, обслуживаемые
этим сотрудником. Здесь мы вновь будем использовать файловую БД Northwind.mdb,
схема связей которой изображена на рисунке, построенном в MS Office Access:

Добавьте к решению ADO командой File/Add/New Project новый проект с именем WinForms2 и
назначьте его стартовым командой Project/Set as StartUp Project
В панели Solution
Explorer создайте папку Data для
корневого узла проекта командой Add/New Folder и скопируйте
в нее перетаскиванием мышью через панель Solution Explorer из предыдущего
проекта файл Northwind.mdb.
При появлении мастера Data Source Configuration Wizard отмените
его кнопкой Cancel
Поместите
на форму из панели Toolbox нужные компоненты и настройте
их в соответствии с таблицей свойств| Компонент | Свойство | Значение |
|---|---|---|
Form |
Text |
Упражнение 2 |
MaximizeBox |
False |
|
Label |
Text |
Сотрудник: |
ComboBox |
(Name) |
cbEmployees |
ListBox |
(Name) |
lstCustomers |
Dock |
Bottom |
Вначале мы заполним раскрывающийся список ComboBox сотрудниками, обслуживающими
заказы. Список будем заполнять в конструкторе формы.
Модифицируйте файл Form1.cs следующим образомusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Дополнительные пространства имен
using System.Data.OleDb;
using System.Data.Common;
using System.Collections;
namespace WinForms2
{
public partial class Form1 : Form
{
// Создаем объект списка сотрудников
ArrayList dbEmployeesList = new ArrayList();// Поле
public Form1()
{
InitializeComponent();
LoadEmployees();
}
// Строка соединения с абсолютным путем к БД, определяемым сборкой
String ConnectionString(String fileName)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileName.Trim() + ".mdb";
}
// Заполнение раскрывающегося списка из БД
void LoadEmployees()
{
// Создаем объект соединения
OleDbConnection objConnection =
new OleDbConnection(ConnectionString("Northwind"));
// Создаем объект команды
OleDbCommand objCommand = new OleDbCommand(
"SELECT EmployeeID, RTRIM(LastName) + ', ' + RTRIM(FirstName) AS FullName FROM Employees",
objConnection);
// Открываем соединение
objConnection.Open();
// Читаем в DbDataReader
OleDbDataReader reader = objCommand.ExecuteReader();
// Заполняем автономный список записей
foreach (DbDataRecord rec in reader)
{
dbEmployeesList.Add(rec);
}
// Закрываем соединение
objConnection.Close();
// Связываем автономный список записей с ComboBox
cbEmployees.BeginUpdate();
cbEmployees.DataSource = dbEmployeesList;
cbEmployees.DisplayMember = "FullName";
cbEmployees.ValueMember = "EmployeeID";
cbEmployees.EndUpdate();
cbEmployees.SelectedIndex = 0;// Устанавливаем на первый элемент
}
}
}
Запустите проект WinForms2 и убедитесь, что раскрывающийся список заполняется информацией из таблицы Employees БД Northwind
Теперь, по выбранному сотруднику, мы сформируем список заказов из таблицы Orders, которые этот сотрудник обслуживает. Причем, в этом списке нам нужен
будет только идентификатор заказчиков, сделавших заказ. В дальнейшем, загружая
информацию из таблицы заказчиков ( Customers ), мы выбирем только тех из них,
которые делали заказы у заданного сотрудника. Останется только отобразить этих
заказчиков пользователю.
Создайте обработчик
события SelectedIndexChanged для объекта cbEmployees и дополните
класс Form1 проекта WinForms2 следующим кодомprivate void cbEmployees_SelectedIndexChanged(object sender, EventArgs e)
{
LoadOrders(); // Выбираем информацию из таблицы Orders
// Прореживаем список
ArrayList tmpList = new ArrayList();
for (int i = 0; i < ordersCustomerID.Count; i++)
{
object val = ordersCustomerID[i];
if (tmpList.IndexOf(val) == -1)
tmpList.Add(val);
}
//ordersCustomerID = tmpList.Clone() as ArrayList;
ordersCustomerID = tmpList; // Бросаем старый список
ordersCustomerID.Sort(); // Сортируем список
lstCustomers.Items.Clear(); // Чистим ListBox
// Отображаем список
for (int i = 0; i < ordersCustomerID.Count; i++)
lstCustomers.Items.Add(ordersCustomerID[i]);
}
// Получение всех закрепленных за сотрудником заказов
ArrayList ordersCustomerID = new ArrayList();
void LoadOrders()
{
if (cbEmployees.SelectedIndex == -1)
return;
// Создаем объект соединения
OleDbConnection objConnection =
new OleDbConnection(ConnectionString("Northwind"));
// Создаем объект команды
OleDbCommand objCommand = new OleDbCommand(
"SELECT CustomerID FROM Orders WHERE (EmployeeID = ?)",
objConnection);
objCommand.Parameters.Add(new OleDbParameter());
objCommand.Parameters[0].Value = cbEmployees.SelectedValue.ToString();
objConnection.Open(); // Открываем соединение
OleDbDataReader reader = objCommand.ExecuteReader();// Читаем в DbDataReader
if (reader.HasRows)
{
// Чистим список
ordersCustomerID.Clear();
while(reader.Read())
{
ordersCustomerID.Add(reader["CustomerID"]);
}
}
objConnection.Close(); // Закрываем соединение
}
Запустите проект -
список lstCustomers заполняется неповторяющимися идентификаторами заказчиков
из поля CustomerIDОбратите внимание, что список lstCustomers заполняется только после
явного выбора служащего в объекте cbEmployees при возбуждении события cbEmployees.SelectedIndexChanged, но при первом запуске формы остается
пустым. Можно это так и оставить, но лучше - пусть заполняется и в самом начале.
Выделите на форме объект cbEmployees,
в панели Properties установите режим Events, найдите событие BindingContextChanged и подпишите его на уже существующий обработчик cbEmployees_SelectedIndexChanged()
Запустите проект -
теперь список заполняется и в самом начале появления формы на экранеЗадача, которую мы поставили в данном упражнении, решена еще не полностью.
Цель заключалась в просмотре заказчиков по выбранному служащему, а пока отображаются
только идентификаторы заказчиков. Теперь нужно из таблицы Customers для
каждого CustomerID, закрепленного за выбранным служащим, запросить
значение поля CompanyName и отобразить в списке lstCustomers.
Модифицируйте класс Form1 проекта WinForms2 следующим
образомusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Дополнительные пространства имен
using System.Data.OleDb;
using System.Data.Common;
using System.Collections;
namespace WinForms2
{
public partial class Form1 : Form
{
// Создаем объект списка сотрудников
ArrayList dbEmployeesList = new ArrayList();// Поле
public Form1()
{
InitializeComponent();
LoadEmployees();
LoadCustomers();
}
// Строка соединения с абсолютным путем к БД, определяемым сборкой
String ConnectionString(String fileName)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileName.Trim() + ".mdb";
}
// Заполнение раскрывающегося списка из БД
void LoadEmployees()
{
// Создаем объект соединения
OleDbConnection objConnection =
new OleDbConnection(ConnectionString("Northwind"));
// Создаем объект команды
OleDbCommand objCommand = new OleDbCommand(
"SELECT EmployeeID, RTRIM(LastName) + ', ' + RTRIM(FirstName) AS FullName FROM Employees",
objConnection);
// Открываем соединение
objConnection.Open();
// Читаем в DbDataReader
OleDbDataReader reader = objCommand.ExecuteReader();
// Заполняем автономный список записей
foreach (DbDataRecord rec in reader)
{
dbEmployeesList.Add(rec);
}
// Закрываем соединение
objConnection.Close();
// Связываем автономный список записей с ComboBox
cbEmployees.BeginUpdate();
cbEmployees.DataSource = dbEmployeesList;
cbEmployees.DisplayMember = "FullName";
cbEmployees.ValueMember = "EmployeeID";
cbEmployees.EndUpdate();
cbEmployees.SelectedIndex = 0;// Устанавливаем на первый элемент
}
private void cbEmployees_SelectedIndexChanged(object sender, EventArgs e)
{
LoadOrders(); // Выбираем информацию из таблицы Orders
// Прореживаем список
ArrayList tmpList = new ArrayList();
for (int i = 0; i < ordersCustomerID.Count; i++)
{
object val = ordersCustomerID[i];
if (tmpList.IndexOf(val) == -1)
tmpList.Add(val);
}
//ordersCustomerID = tmpList.Clone() as ArrayList;
ordersCustomerID = tmpList; // Бросаем старый список
ordersCustomerID.Sort(); // Сортируем список
lstCustomers.Items.Clear(); // Чистим ListBox
// Отображаем список
for (int i = 0; i < ordersCustomerID.Count; i++)
{
//lstCustomers.Items.Add(ordersCustomerID[i]);
int index = customerID.IndexOf(ordersCustomerID[i]);
lstCustomers.Items.Add(customerID[index] + "\t - " + companyName[index]);
}
}
// Читаем таблицу заказчиков Customers
ArrayList companyName = new ArrayList();
ArrayList customerID = new ArrayList();
void LoadCustomers()
{
OleDbConnection objConnection =
new OleDbConnection(ConnectionString("Northwind"));
OleDbCommand objCommand = new OleDbCommand(
"SELECT CustomerID, CompanyName FROM Customers",
objConnection);
objConnection.Open();
OleDbDataReader reader =
objCommand.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
customerID.Add(reader.GetString(0));
companyName.Add(reader.GetString(1));
}
reader.Close();
}
// Получение всех закрепленных за сотрудником заказов
ArrayList ordersCustomerID = new ArrayList();
void LoadOrders()
{
if (cbEmployees.SelectedIndex == -1)
return;
// Создаем объект соединения
OleDbConnection objConnection =
new OleDbConnection(ConnectionString("Northwind"));
// Создаем объект команды
OleDbCommand objCommand = new OleDbCommand(
"SELECT CustomerID FROM Orders WHERE (EmployeeID = ?)",
objConnection);
objCommand.Parameters.Add(new OleDbParameter());
objCommand.Parameters[0].Value = cbEmployees.SelectedValue.ToString();
objConnection.Open(); // Открываем соединение
OleDbDataReader reader = objCommand.ExecuteReader();// Читаем в DbDataReader
if (reader.HasRows)
{
// Чистим список
ordersCustomerID.Clear();
while(reader.Read())
{
ordersCustomerID.Add(reader["CustomerID"]);
}
}
objConnection.Close(); // Закрываем соединение
}
}
}
Запустите проект -
все работает как задумали
Объект System.Data.DataTable применяется для работы с одной таблицей
из БД. Класс DataTable содержит три важных коллекции:
Columns - содержит ноль и более объектов DataColumn, которые определяют
имя, тип хранимых данных и первичный ключ каждого столбца из DataTable Rows - содержит ноль и более объектов DataRow, которые содержат
реальные записи таблицы загруженных данныхConstraints - содержит объекты типа ForeignKeyConstraint или UniqueConstraint.
Первые определяют действия, выполняемые над ключевым столбцом при изменении
или удалении строки, вторые используются для обеспечения уникальности всех
значений в данном столбцеОбъекты DataTable могут быть частью набора DataSet, но могут существовать
и отдельно. Объект DataTable может заполняться адаптером данных точно также,
как и объект DataSet - с помощью метода Fill() адаптера. Для адаптера данных
не нужно специально открывать соединение с БД, оно устанавливается автоматически
при работе метода Fill() и сразу закрывается после завершения работы метода. DataTable предназначен для работы в качестве автономного кэша табличных данных.
Загруженные в DataTable данные можно динамически связывать с элементами отображения.
В данном упражнении мы соединимся с таблицей Customers БД и загрузим из нее
в отображаемый список имена заказчиков CompanyName по начальному словосочетанию
ключа CustomerID. При этом для отключенного хранения данных в приложении будем
использовать объект DataTable.
Добавьте к решению
новый проект оконного приложения с именем WinForms3 и
назначьте его стартовым
Создайте в корне проекта
папку Data и скопируйте в нее из предыдущего проекта файл Northwind.mdb (при
этом отклоните предложение мастера конфигурации источника данных)
Поместите на форму
компоненты в соответствии с таблицей| Компонент | Свойство | Значение |
|---|---|---|
Form |
Text |
Упражнение 3 |
MaximizeBox |
False |
|
Label |
Text |
Часть ID заказчика: |
TextBox |
(Name) |
txtCustomerID |
Text |
пусто | |
ListBox |
(Name) |
lstCustomers |
Dock |
Bottom |
Модифицируйте файл Form1.cs проекта WinForms3 следующим
образомusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Дополнительные пространства имен
using System.Data.OleDb;
using System.Data.Common;
using System.Collections;
namespace WinForms3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
LoadCustomers();
}
// Строка соединения с абсолютным путем к БД, определяемым сборкой
String ConnectionString(String fileName)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileName.Trim() + ".mdb";
}
OleDbDataAdapter adapter; // Создадим в методе LoadCustomers()
DataTable table = new DataTable();
void LoadCustomers()
{
String strCommand = "SELECT CustomerID, CompanyName FROM Customers "
+ "WHERE CustomerID LIKE '" + txtCustomerID.Text.Trim() + "%'";
try
{
adapter = new OleDbDataAdapter(strCommand, ConnectionString("Northwind"));
table.Clear();// Надо очистить, а то будет накопление !!!
adapter.Fill(table);
lstCustomers.Items.Clear();// Очищаем список
foreach (DataRow row in table.Rows)
{
lstCustomers.Items.Add(
row["CustomerID"] + "\t - " + row["CompanyName"]);
}
/*
// Связываем прочитанные данные с элементом отображения
//lstCustomers.Items.Clear(); // Нельзя очищать связанный список!!!!!
lstCustomers.DataSource = table;
lstCustomers.DisplayMember = "CompanyName";
lstCustomers.ValueMember = "CustomerID";
*/
}
catch (OleDbException exc)
{
MessageBox.Show(exc.Message, "Ошибка",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
При выборке данных могут возникнуть ошибки времени выполнения, поэтому мы этот код упаковали в блок обработки исключений. Например, при ошибке в шаблоне запроса будет выдано соответствующее сообщение, но приложение останется работоспособным

Предикат LIKE команды
задает шаблон ' xx% ' проверки соответствия начальной фразы с любым
последующим количеством символов поля CustomerID. Шаблон ' %xx% '
выбирает записи с установленной фразой в любом месте поля поиска. Если в
шаблон с LIKE передается пустая строка, то будут выбраны
все записи указанной таблицы.
Создайте обработчик
события KeyDown для текстового поля txtCustomerID с тем, чтобы
список обновлялся по нажатию пользователем клавиши Enter при
завершении ввода очередного значения параметра, и заполните его следующим
кодомprivate void txtCustomerID_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
LoadCustomers();
}
Запустите приложение
и поэкспериментируйте с его функциональностью, заложенной нами. Одно из представлений
будет таким
Из кода видно, что объект DataTable является очень удобным средством
работы с данными в отсоединенном режиме, как и вся инфраструктура ADO.NET в
целом.
Объект DataTable - это находящееся в кэше памяти представление выбранных
из БД данных в табличной форме. Он может быть заполнен объединением одной или
нескольких таблиц из базы данных. Первоначальное заполнение DataTable выполняется
адаптером в соответствии с SQL-запросом. Но после первоначальной загрузки некоторые
столбцы могут быть добавлены в DataTable уже как результат вычислительных
действий над исходными столбцами в отсоединенном режиме.
Можно по исходным таблицам создавать новые виртуальные таблицы, готовя их для отображения пользователю в соответствии с уровнем бизнес-логики приложения. Эти таблицы можно наделять свойствами физических таблиц, назначая им ключевые поля и устанавливая отношения между ними типа главная-дочерняя.
Есть два пути выборки нескольких строк или поиска конкретной строки таблицы:
либо сформировать SQL-запрос к исходной таблице БД с заданным условием, либо
загрузить исходную таблицу в кэш и уже в нем выполнять требуемую выборку. Объект DataTable имеет
встроенные средства выборки нескольких строк из таблицы кэша с их сортировкой
или поиска конкретной строки. В последнем случае применяется метод Find(),
который работает только со столбцом, содержащим значения первичного ключа.
Поэтому перед вызовом метода Find() нужно либо загрузить схему, либо
вручную указать первичный ключ. Результатом выборки будет новая таблица, строка
или массив строк.
В данном упражнении мы будем работать с таблицей Customers, схема которой
показана на рисунке

SQL-командой мы полностью загрузим в кэш все столбцы таблицы Customers.
В отсоединенном
режиме по выбранному CustomerID будем
искать соответствующую запись и показывать пользователю значения
полей CustomerID, CompanyName, Adress, City, Region и Phone виртуальной
таблицы. Столбцы CustomerID и CompanyName динамически
свяжем с раскрывающимся списком ComboBox, причем столбец CompanyName сделаем
видимым. Значения полей найденной записи будем отображать в элементах TextBox,
недоступных для редактирования. Столбцу CustomerID присвоим статус первичного
ключа виртуальной таблицы.
Добавьте к решению
новый проект оконного приложения с именем WinForms4,
назначьте его стартовым и создайте пользовательский интерфейс в соответствии
с таблицей| Объект | Свойство | Значение | Пояснения |
|---|---|---|---|
Form1 |
Text |
Упражнение 4 |
Заголовок окна |
MaximizeBox |
False |
Отключили системную кнопку | |
FormBorderStyle |
FixedSingle |
Размеры окна менять нельзя | |
Label |
Text |
Customer: |
|
ComboBox |
(Name) |
cbCustomers |
|
DropDownStyle |
DropDownList |
Текст в поле списка редактировать нельзя | |
Label |
Text |
Customer ID: |
|
TextBox |
(Name) |
txtCustomerID |
|
Label |
Text |
Company Name: |
|
TextBox |
(Name) |
txtCompanyName |
|
Label |
Text |
Address: |
|
TextBox |
(Name) |
txtAddress |
|
Label |
Text |
City: |
|
TextBox |
(Name) |
txtCity |
|
Label |
Text |
Region: |
|
TextBox |
(Name) |
txtRegion |
|
Label |
Text |
Phone: |
|
TextBox |
(Name) |
txtPhone |
|
txtCustomerID, txtCompanyName, txtAddress, txtCity, txtRegion, txtPhone |
BorderStyle |
FixedSingle |
Простая рамка вокруг текстового поля |
Форма на этапе проектирования должна выглядеть так

Защитите текстовые
поля от редактирования, для этого: выделите на форме
все элементы TextBox и в панели Properties в
режиме Events создайте для них общий обработчик события KeyPress,
затем общий обработчик события KeyDown. Обработчики заполните такprivate void txtCustomerID_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = true;
}
private void txtCustomerID_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
В панели Solution
Explorer добавьте к корневому узлу проекта WinForms4 папку Data и перетащите
в нее мышью из соседнего проекта файл Northwind.mdb (при этом отклоните
предложение мастера конфигурации источника данных)
Добавьте в начало файла Form1.cs подключения пространства имен сборки System.Data.dll,
нужные для обеспечения видимости библиотечных классов// Дополнительные пространства имен using System.Data.OleDb; using System.Data.Common; using System.Collections;
Добавьте в класс Form1 метод LoadCustomers(), который загружает таблицу Customers из
БД с помощью динамически созданного поставщика данных и SQL-запроса// Строка соединения с абсолютным путем к БД, определяемым сборкой
String ConnectionString(String fileName)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileName.Trim() + ".mdb";
}
DataTable tableCustomers;
void LoadCustomers()
{
OleDbCommand selectCommand = new OleDbCommand();
selectCommand.CommandText="SELECT * FROM Customers";
selectCommand.Connection = new OleDbConnection(ConnectionString("Northwind"));
selectCommand.CommandType = CommandType.Text;
OleDbDataAdapter adapter = new OleDbDataAdapter(selectCommand);
tableCustomers = new DataTable();// Создать таблицу
try
{
// Заполнить таблицу
adapter.Fill(tableCustomers);
// Указать первичный ключ из одного столбца для использования метода Find()
tableCustomers.PrimaryKey =
new DataColumn[] { tableCustomers.Columns["CustomerID"] };
}
catch (OleDbException exc)
{
tableCustomers = null;
MessageBox.Show(exc.Message, "Ошибка",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
selectCommand.Connection.Close();
}
// Связываем ComboBox с виртуальной таблицей
cbCustomers.DataSource = tableCustomers;
cbCustomers.DisplayMember = "CompanyName";
cbCustomers.ValueMember = "CustomerID";
}
Вставьте вызов метода LoadCustomers() в
конструктор класса Form1 проекта WinForms4public Form1()
{
InitializeComponent();
LoadCustomers();
}
Выделите объект cbCustomers и
создайте для его события SelectedIndexChanged обработчик. Этот же
обработчик зарегистрируйте и для события BindingContextChanged объекта cbCustomers.
Заполните обработчик следующим кодомprivate void cbCustomers_SelectedIndexChanged(object sender, EventArgs e)
{
if (tableCustomers == null)
return; // При исключении выходить
// Найти запись
Object findValue = cbCustomers.SelectedValue;// Выделенное значение ValueMember
DataRow row = tableCustomers.Rows.Find(findValue);
// Заполнить текстовые поля
if (row != null)
{
txtCustomerID.Text = row["CustomerID"].ToString();
txtCompanyName.Text = row["CompanyName"].ToString();
txtAddress.Text = row["Address"].ToString();
txtCity.Text = row["City"].ToString();
txtRegion.Text = row["Region"].ToString();
txtPhone.Text = row["Phone"].ToString();
}
}
Запустите проект: список
заполняется сразу при запуске проекта, текстовые поля отображают подробности
при выборе заказчика и недоступны для редактирования, как и поле ComboBox 
Поиск строки в виртуальной таблице DataTable выполняется методом Find() по
столбцу CustomerID, назначенному нами первичным ключем. Без этого метод работать
не будет. Критерий поиска берется из выбранного пользователем значения списка.
Задача решена.
Выборку строк по определенному критерию традиционно можно выполнить с помощью
SQL-запроса. Например, можно запросить всех заказчиков из таблицы Customers,
имена которых начинаются на " My ". с помощью такой команды
SELECT * FROM Customers WHERE FirstName LIKE 'My%
Или запросить всех заказчиков, имена которых заканчиваются на " jon "
SELECT * FROM Customers WHERE FirstName LIKE '%jon'
Если нужно выполнять много подобных запросов к одной и той же таблице с помощью
SQL, то для этого потребуется часто соединяться с БД, которая может быть занята
запросами других пользователей. Хорошо бы было загрузить в кэш полную
таблицу и обращаться к ней с запросами сколько угодно раз,
не обращаясь к самой центральной БД. Для таких целей предусмотрен метод Select() объекта DataTable,
который возвращает коллекцию строк, удовлетворяющих условию выборки. В методе
можно также задавать и условие сортировки результирующего набора данных.
В данном упражнении поставим следующую задачу. В раскрывающийся список считаем
из БД всех служащих из таблицы Employees. Загрузим в кэш полную таблицу заказов Orders и дадим возможность пользователю просматривать из этой таблицы те заказы,
которые оформил конкретный служащий, выбранный в раскрывающемся списке.
Добавьте к решению
новый проект оконного приложения с именем WinForms5 и
назначьте его стартовым
В панели Solution
Explorer в корневой узел проекта WinForms5 перетащите мышью
из соседнего проекта папку Data с файлом Northwind.mdb (при
этом отклоните предложение мастера конфигурации источника данных)
Добавьте в начало файла Form1.cs подключения
пространства имен сборки System.Data.dll, нужные для обеспечения видимости
библиотечных классов// Дополнительные пространства имен using System.Data.OleDb; using System.Data.Common; using System.Collections;
Оформите пользовательский
интерфейс формы в соответствии с таблицей| Объект | Свойство | Значение | Пояснения |
|---|---|---|---|
Form1 |
Text |
Упражнение 5 |
Заголовок окна |
MaximizeBox |
False |
Отключили системную кнопку | |
FormBorderStyle |
FixedSingle |
Размеры окна менять нельзя | |
Label |
Text |
Employee: |
|
ComboBox |
(Name) |
cbEmployees |
|
DropDownStyle |
DropDownList |
Текст в поле списка редактировать нельзя | |
ListBox |
(Name) |
lstOrders |
|
Dock |
Bottom |
Форма на этапе проектирования должна выглядеть так

Создайте для события SelectedIndexChanged объекта cbEmployees обработчик и его же присоедините
к событию BindingContextChanged для начального заполнения списка lstOrders
Добавьте
в класс Form1 метод LoadTables(),
который загружает таблицы Employees и Orders из БД с помощью
динамически созданного поставщика данных и SQL-запроса, а также связывает
раскрывающийся список с таблицей EmployeesПолный код файла Form1 для проекта WinForms5 будет примерно таким
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Дополнительные пространства имен
using System.Data.OleDb;
using System.Data.Common;
using System.Collections;
namespace WinForms5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
LoadTables();
}
//***********************************************************
// Строка соединения с абсолютным путем к БД, определяемым сборкой
// Не используем !!!!!! Заменили объектом-построителем
String ConnectionString(String fileName)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;"
+ "Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileName.Trim() + ".mdb";
}
//**********************************************************
// Поле-ссылка на таблицу Orders
DataTable tableOrders;
void LoadTables()
{
// Используем построитель строки подключения
// для облегчения формирования оной!
OleDbConnectionStringBuilder objConnectionStringBuilder =
new OleDbConnectionStringBuilder();
objConnectionStringBuilder.Provider = "Microsoft.Jet.OLEDB.4.0";
objConnectionStringBuilder.DataSource =
Application.StartupPath.ToString() + @"\Data\Northwind.mdb";
objConnectionStringBuilder.PersistSecurityInfo = true;// Шифровать строку
// Строка соединения с источником данных
String strConnection = objConnectionStringBuilder.ToString();
// Строка запроса
String strSQL = "SELECT EmployeeID, LTRIM(RTRIM(LastName)) + ', '"
+ "+ LTRIM(RTRIM(FirstName)) AS FullName FROM Employees";
// Создаем поставщик данных для таблицы Employees
OleDbDataAdapter adapter = new OleDbDataAdapter(strSQL, strConnection);
// Создаем и заполняем виртуальную таблицу
DataTable tableEmployees = new DataTable();
adapter.Fill(tableEmployees);
tableEmployees.TableName = "Employees";
// Сортируем в прямом порядке: "FullName" или "FullName ASC"
// Сортировка в обратном порядке: "FullName DESC";
tableEmployees.DefaultView.Sort = "FullName ASC";
// Связываем ComboBox с виртуальной таблицей
//cbEmployees.DataSource = tableEmployees; //Связать таблицу несортированной
// Связать через DataView сортированной
cbEmployees.DataSource = tableEmployees.DefaultView;
cbEmployees.DisplayMember = "FullName";
cbEmployees.ValueMember = "EmployeeID";
cbEmployees.SelectedIndex = 0;
// Заполняем виртуальную таблицу Orders всеми записями
OleDbCommand selectCommand = new OleDbCommand(
"SELECT OrderID, EmployeeID, ShipAddress FROM Orders");
selectCommand.Connection = new OleDbConnection(strConnection);
adapter = new OleDbDataAdapter(selectCommand);
tableOrders = new DataTable();
adapter.Fill(tableOrders);
}
private void cbEmployees_SelectedIndexChanged(object sender, EventArgs e)
{
if (tableOrders == null)
return;
// Выбираем строки по условию
String filterExpression = "EmployeeID = " + cbEmployees.SelectedValue;
DataRow[] rows = tableOrders.Select(
filterExpression, // Выражение для фильтра
"ShipAddress ASC"); // Поле сортировки по возрастанию
// "ShipAddress DESC" - сортировка по убыванию!!!
// Очищаем список
lstOrders.Items.Clear();
// Заполняем список
foreach (DataRow row in rows)
{
lstOrders.Items.Add(
"(" + row["EmployeeID"] + ") "
+ row["OrderID"].ToString()
+ "\t" + row["ShipAddress"]);
}
}
}
}
Запустите проект -
по выбранному в объекте cbEmployees сотруднику производится выборка закрепленных
за ним заказов из таблицы Orders и заполнение списка новой информацией. Соединение
с БД выполняется только один раз при формировании таблиц в кэшеМы старались разнообразить код. Например, для формирования
строки подключения мы воспользовались вспомогательным классом OleDbConnectionStringBuilder,
а при создании адаптера применяли разные перегрузки его конструктора. Метод Select() объекта DataTable производит выборку строк по выражению,
которое равноценно условию, применяемому в SQL-запросе после ключевого слова WHERE.
Меняя это выражение можно получать различные представления объектной модели
таблицы данных.
Данные, которые хранятся в БД в табличной форме, представляют собой базовую информацию. Но некоторые данные, интересующие пользователя, можно получить путем выполнения определенных действий над исходными данными и показать результат в отдельном столбце таблицы представления. Для этого существуют различные варианты SQL-запросов, включающие в себя различные функции и агрегатные выражения. Например, команда
SELECT EmployeeID, LastName + ', ' + FirstName AS FullName FROM Employees
формирует в кэше из таблицы Employees два столбца, первый из которых
представляет столбец EmployeeID исходной таблицы, а второй - объединение
данные двух исходных столбцов под псевдонимом FullName.
Команда
SELECT ProductID, ProductName, UnitPrice + UnitPrice * ? AS (Total Price)
FROM Products
к двум исходным столбцам из таблицы Products добавляет вычислимый столбец TotalPrice, в котором содержится отпускная цена товара с учетом процента
ставки налога, причем процент налога передается в команду как параметр.
Выборку данных выполняет поставщик на стороне базы данных и в кэш поступают уже готовые данные. Дополнительный столбец пересылается в открытом потоке в память компьютера, что может перегружать трафик при большом количестве запросов и большом объеме загружаемых данных. В ADO.NET существуют средства выполнения подобной работы на стороне клиента, когда в компьютер поступают базовые столбцы таблицы и на лету к ним присоединяются вычислимые сторбцы, нужные пользователю для работы.
В данном упражнении рассматриваются средства присоединения к загружаемой таблице
дополнительных столбцов,
вычисляемых в коде приложения. Упражнение построено на примере вычисления отпускной
цены товара из таблицы Products. Структура данных таблицы Products приведена
на рисунке

Вычислимые столбцы нужно добавлять к объекту таблицы до заполнения ее данными
из поставщика. Отдельные столбцы представляются в объектной модели таблицы
классом DataColumn. Новый объект DataColumn добавляется в таблицу
методом Add() свойства-коллекции DataTable.Columns.
Добавляемый в таблицу объект столбца должен содержать
имя столбца (свойство ColumnName ), тип данных (свойство DataType ) и выражение
(свойство Expression ) при необходимости. Выражение используется для фильтрации
строк, вычисления значений в столбцах на основании других столбцов или для
создания агрегированного (объединенного) столбца. Эти данные передаются в конструктор
при создании объекта столбца или сразу в одну из перегрузок метода Add() при
добавлении в коллекцию столбцов таблицы.
Типы данных, которые могут быть значениями свойства DataColumn.DataType,
ограничиваются следующим списком пространства имен System:
Boolean (bool) |
Decimal |
Int64 |
TimeSpan |
Byte (byte) |
Double (double) |
SByte |
UInt16 |
Char (char) |
Int16 |
Single (float) |
UInt32 |
DateTime |
Int32 (int) |
String |
UInt64 |
Свойство DataColumn.DefaultValue показывает, какое значение имеет по
умолчанию данный столбец. Если столбцу не присвоено никакого значения, а значение
по умолчанию не определено, то столбец имеет значение System.DBNull.
Поскольку типы данных реляционной таблицы БД и виртуальной таблицы объектной
модели могут быть разными, рекомендуется явно привести тип столбца к нужному
вычислимому типу. И это нужно сделать до загрузки объектной таблицы!!!
Таблица DataTable, помимо прочего, имеет три коллекции в качестве свойств
только для чтения:
Columns - содержит объекты столбцов вместе с их именами и типомRows - содержит фактические данные, упакованные в объектах строкConstraints - содержит экземпляры классов ForeignKeyConstraint или UniqueConstraint,
задающих свойства в отношении ключевых или уникальных значений полейПосле добавления вычислимого столбца в коллекцию Columns объекта таблицы
к нему можно обращаться в Columns по имени или порядковому номеру. Для
иллюстрации сказанного выполните данное упражнение в следующем порядке:
Добавьте к решению
проект оконного приложения с именем WinForms6 и назначьте его стартовым
Настройте пользовательский
интерфейс в соответствии с таблицей| Компонент | Свойство | Значение |
|---|---|---|
Form |
Text |
Упражнение 6 |
|
MaximizeBox |
False |
DataGridView |
(Name) |
dataGridView |
|
Dock |
Fill |
|
AutoSizeColumnsMode |
Fill |
Заполните файл Form1.cs проекта WinForms6 следующим образомusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Дополнительные пространства имен
using System.Data.OleDb;
using System.Data.Common;
using System.Collections;
namespace WinForms6
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
LoadProducts();
}
// Строка соединения к БД с абсолютным путем, определяемым сборкой
String ConnectionString()
{
// Используем построитель строки подключения
OleDbConnectionStringBuilder objConnectionStringBuilder =
new OleDbConnectionStringBuilder();
objConnectionStringBuilder.Provider = "Microsoft.Jet.OLEDB.4.0";
objConnectionStringBuilder.DataSource =
Application.StartupPath.ToString() + @"\Data\Northwind.mdb";
return objConnectionStringBuilder.ToString();
}
void LoadProducts()
{
// Формируем поставщик
OleDbCommand selectCommand = new OleDbCommand();
selectCommand.CommandText =
"SELECT ProductID, ProductName, UnitPrice AS Price FROM Products";
selectCommand.Connection = new OleDbConnection(ConnectionString());
selectCommand.CommandType = CommandType.Text;
OleDbDataAdapter adapter = new OleDbDataAdapter(selectCommand);
// Создаем объект таблицы
DataTable tableProducts = new DataTable();
// Задаем тип столбца, подлежащего заполнению данными из БД
// (ДО ЕГО ЗАПОЛНЕНИЯ!!!)
tableProducts.Columns.Add("Price");
tableProducts.Columns["Price"].DataType = typeof(System.Decimal);
// Добавляем и настраиваем вычислимый столбец для величины наценки
tableProducts.Columns.Add("Margin", typeof(System.Decimal));
tableProducts.Columns[1].Expression = "Price * 0.086";
// Добавляем и настраиваем вычислимый столбец для потребительской цены
DataColumn totalPrice = new DataColumn("Total Price");
totalPrice.DataType = System.Type.GetType("System.Decimal");
totalPrice.Expression = "Price + Margin";
tableProducts.Columns.Add(totalPrice);
// Заполняем таблицу
adapter.Fill(tableProducts);
// Связываем таблицу с элементом отображения
dataGridView.DataSource = tableProducts;
Object result = tableProducts.Compute("Sum(Margin)", null);
MessageBox.Show(String.Format("Суммарная наценка на товар\n"
+ "(по курсу 30 рублей за доллар): {0:C}",// Спецификатор денежного формата
Convert.ToDecimal(result) * 30.0m));
result = tableProducts.Compute("Count(Price)", "Price < 10");
MessageBox.Show(String.Format("Количество товара с ценой меньше 10$: {0} шт.", result));
}
}
}
Запустите приложение
- результат будет таким
Объект DataView выполняет представление одних и тех же данных загруженной
таблицы БД в нескольких разных форматах по принципу "Один документ - много
представлений". DataView работает только с таблицей DataTable и готовит
для отображения все ее столбцы без исключения. В присоединенном к таблице объекте DataView данные можно сортировать или задавать некоторые условия
выборки строк представляемой таблицы. DataView физически не включает в себя
данные отображаемой таблицы, он только изменяет их представление в элементе
отображения "на лету".
Можно для одной таблицы заготовить несколько
объектов DataView, каждый со своими настройками, и поочередно связывать их
с элементом отображения. При этом в памяти компьютера будет находиться только
одна копия данных, что существенно экономит ресурсы. Сам экземпляр объекта DataView можно настроить так, чтобы он запрещал редактирование данных связанной
с ним таблицы, хотя в другом объекте такие ограничения на ту же самую таблицу
могут отсутствовать. Операцию вставки, обновления или удаления данных в связанной
таблице разрешают (запрещают) через свойства AllowNew, AllowEdit, AllowDelete.
Если флаг AllowNew разрешает вставку, то новая строка добавляется в DataView
методом AddNew().
При заполнении таблицы DataTable программно или данными БД строки отображаются
в том порядке, в котором они были вставлены в таблицу или извлечены из БД.
С любой таблицей уже связан один объект DataView, ссылка на который носит имя
свойства DefaultView только для чтения. Через этот объект-представление мы
можем сортировать представление данных таблицы и связывать элемент отображения
не с самой таблицей, а с ее отсортированным представлением - объектом DefaultView.
Если встроенного представления нам мало, можно создать и настроить еще ряд
объектов DataView и по мере надобности подключать их к свойству DataSource
элемента отображения данных. Связанную с элементом отображения таблицу сортировать
нельзя, как и сам связанный с таблицей элемент отображения. Но если между
таблицей и элементом отображения имеется посредник DataView, то фильтрацию
и сортировку отображаемых данных исходной таблицы можно выполнять через него.
У каждого элемента DataRow есть свойство RowState, в котором ведется история
состояния строки при ее редактировании. Это свойство автоматически принимает
одно из значений перечисления DataRowState при выполнении действий над строкой:
Detached - строка только что создана методом DataTable.NewRow(), но еще
не добавлена в таблицу методом DataTable.Rows.Add()Added - строка добавлена в коллекцию таблицы, но не помечена как окончательно
принятая один из методов: DataRow.AcceptChanges(), DataTable.AcceptChanges(), DataSet.AcceptChanges()Deleted - строка помечена как удаленная из таблицы методом DataRow.Delete(),
но не принята окончательно методом AcceptChanges()Modified - строка была изменена, но еще не принятаUnchanged - строка не изменилась со времени последнего вызова AcceptChanges()Объектная модель таблицы хранит несколько версий одной и той же строки. Эти
версии строки зависят от того, применялся или нет к ней метод AcceptChanges().
Значения версий определяются перечислением DataRowVersion и могут иметь значения: Current, Default, Original, Proposed. Какие версии имеет строка, можно проверить
с помощью ее метода DataRow.HasVersion(DataRowVersion), например, следующий
код распечатает все существующие версии указанной строки
void CheckVersion(DataRow row)
{
if (row.HasVersion(DataRowVersion.Current))
Console.WriteLine("Версия Current");
if (row.HasVersion(DataRowVersion.Default))
Console.WriteLine("Версия Default");
if (row.HasVersion(DataRowVersion.Original))
Console.WriteLine("Версия Original");
if (row.HasVersion(DataRowVersion.Proposed))
Console.WriteLine("Версия Proposed");
}
Указанные версии строки связаны с ее редактированием и вызовом метода AcceptChanges()
объектов DataRow, DataTable или DataSet следующим образом:
DataRow.BeginEdit(), если значение строки будет изменено,
станут доступными версии Current и Proposed (текущий и предложенный)DataRow.CancelEdit() будет удалена версия Proposed DataRow.EndEdit() версия Proposed становится версией Current DataRow.AcceptChanges() версия Proposed становится
версией Current ; версия Original не изменяетсяDataTable.AcceptChanges() версия Original становится
идентичной версии Current DataTable.RejectChanges() (- отклонить изменения) версия Original становится версией Current Объект DataView имеет свойства, позволяющие управлять представляемыми данными:
RowFilter - позволяет задать критерий выбора представляемых строк, напримерRowFilter="Price < 10"; RowFilter="LastName LIKE ' W%' "; RowFilter="ProductID = 1"
RowStateFilter - позволяет выбирать строки, находящиеся в определенном
состоянии. Значение этого свойства определяется перечислением System.Data.DataViewRowState:Added - новые строкиCurrentRows - текущие строки, включая неизмененные, новые и модифицированныеDeleted - строки, помеченные как удаленныеModifiedCurrent - ближайшая модифицированная версия данных, полученная
из ModifiedOriginalModifiedOriginal - предыдущая версия данных, из которой получена
версия ModifiedCurrentNone - пустое значениеOriginalRows - первоначальные строки, загруженные из БДUnchanged - строка не меняласьНапример, следующая функция настраивает DataView на представление строк таблицы предыдущего или следующего
состояния модификации
DataView ViewChanges(DataTable table, bool viewCurrent)
{
DataView view = table.DefaultView;
if (viewCurrent == true)
view.RowStateFilter = DataViewRowState.ModifiedCurrent;
else
view.RowStateFilter = DataViewRowState.ModifiedOriginal;
return view;
}
Sort - это свойство указывает порядок сортировки представляемых
данных, например Sort="FullName ASC" ; Sort="FullName DESC" ;
- сортируют в прямом и обратном порядке представление таблицы по полю FullName Объект DataView имеет два метода поиска данных для выборки в представление,
оба из которых осуществляют поиск в отсортированном (в свойстве Sort ) столбце
по заданному критерию:
DataView.Find() - ищет первое вхождение строки в DataView и возвращает
номер строки, или -1DataView.FindRows() - ищет массив строк и возвращает его, или пустой массивВ данном упражнении элементы отображения мы свяжем не напрямую с таблицей,
заполненной данными, а с промежуточным объектом DataView, в котором легко можно
менять представления данных пользователю. В коде попробуем продемонстрировать
основные возможности объекта DataView. В режиме проектирования интерфейса установим
простейшие настройки его элементов, а остальное выполним динамически в коде
приложения.
Добавьте в решение
новый проект с именем WinForms7 и оформите
интерфейс пользователя в соответствии с таблицей| Объект | Свойство | Значение | Пояснения |
|---|---|---|---|
Form |
Text |
Упражнение 7 |
Заголовок окна |
MaximizeBox |
False |
Запретить системную кнопку | |
Panel |
Dock |
Top |
Верхний контейнер для Label и ComboBox |
Panel |
Dock |
Fill |
Нижний контейнер для DataGrid |
Label |
Text |
Letter: |
Объекты Label и ComboBox поместите в верхний контейнер Panel |
ComboBox |
(Name) |
cbLetters |
Для размещения букв латинского алфавита |
FlatStyle |
Flat |
Плоский стиль элемента | |
Label |
Text |
Column: |
|
ComboBox |
(Name) |
cbColumns |
Для размещения имен столбцов |
DataGrid |
(Name) |
dataGrid |
Объект DataGrid поместите в нижний контейнер Panel |
AllowSorting |
False |
Запретить сортировку столбцов через DataGrid |
|
CaptionText |
Представления таблицы Customers |
Заголовок DataGrid |
|
Dock |
Fill |
Развернуть на весь контейнер Panel |
|
ReadOnly |
True |
Запретить редактирование данных |
Модифицируйте файл Form1.cs следующим образомusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Дополнительные пространства имен
using System.Data.OleDb;
using System.Data.Common;
namespace WinForms7
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Подписка на события ComboBox общего обработчика
cbLetters.SelectedIndexChanged += Combo_SelectedIndexChanged;
cbColumns.SelectedIndexChanged += Combo_SelectedIndexChanged;
// Загрузки объектов данными
LoadCustomers();
LoadLetters();
LoadColumns();// Вызывать после загрузки данных LoadCustomers()
}
// Строка соединения к БД с абсолютным путем, определяемым сборкой
String ConnectionString()
{
// Используем построитель строки подключения
OleDbConnectionStringBuilder objConnectionStringBuilder =
new OleDbConnectionStringBuilder();
objConnectionStringBuilder.Provider = "Microsoft.Jet.OLEDB.4.0";
objConnectionStringBuilder.DataSource =
Application.StartupPath.ToString() + @"\Data\Northwind.mdb";
return objConnectionStringBuilder.ToString();
}
DataTable tableCustomers;// Поле, видимое в методах
void LoadCustomers()
{
// Формируем поставщик
OleDbCommand selectCommand = new OleDbCommand();
selectCommand.CommandText =
"SELECT * FROM Customers";// Будем читать все столбцы
selectCommand.Connection = new OleDbConnection(ConnectionString());
selectCommand.CommandType = CommandType.Text;// По умолчанию
OleDbDataAdapter adapter = new OleDbDataAdapter(selectCommand);
// Создаем объект таблицы
tableCustomers = new DataTable();
// Заполняем таблицу
adapter.Fill(tableCustomers);
// Адресуемся к встроенному в таблицу представлению
DataView defaultView = tableCustomers.DefaultView;
// Связываем встроенное представление с элементом отображения
dataGrid.DataSource = defaultView;
}
void LoadLetters()
{
// Добавили первый элемент списка
cbLetters.Items.Add("All");
// Дополняем список буквами алфавита
for (char ch = 'A'; ch <= 'Z'; ch++)
{
cbLetters.Items.Add(ch);
}
// Устанавливаем на первый индекс
cbLetters.SelectedIndex = 0;
}
void LoadColumns()
{
// Создаем временную таблицу для имен столбцов
DataTable tableTmp = new DataTable();
// Добавляем один столбец для хранения имен столбцов
tableTmp.Columns.Add("ColumnName", typeof(String));
// Заполняем временную таблицу именами столбцов исходной таблицы
for (int i = 0; i < tableCustomers.Columns.Count; i++)
{
tableTmp.Rows.Add(tableCustomers.Columns[i].ColumnName);
}
// Создаем представление по единственному столбцу
DataView view = new DataView(tableTmp);
// Связываем представление с элементом отображения
cbColumns.DataSource = view;
cbColumns.DisplayMember = "ColumnName";
// Ищем нужное имя и позиционируем список
view.Sort = "ColumnName";// Сортируем для списка имен столбцов
int selectedIndex = view.Find("CustomerID");// По отсортированному можно
cbColumns.SelectedIndex = selectedIndex;// Позиционируем программно на CustomerID
}
private void Combo_SelectedIndexChanged(object sender, EventArgs e)
{
// Еще не созданы
if (cbLetters == null || cbColumns == null)
return;
// Еще ничего не выбрано
if (cbLetters.SelectedIndex == -1 || cbColumns.SelectedIndex == -1)
return;
// Извлекаем символ из cbLetters
String letter = cbLetters.SelectedItem.ToString();
if (letter == "All")
letter = String.Empty;
// Извлекаем из cbColumns, связанного с DataView, выбранное имя столбца
Object obj = cbColumns.SelectedItem;
String columnName = ((DataRowView)obj).Row["ColumnName"].ToString();
// Создаем для таблицы Customers объект представления и настраиваем его
DataView view = new DataView(tableCustomers);
view.RowFilter = "CustomerID LIKE '" + letter + "%'";
view.Sort = columnName;
// Связываем настроенное представление с объектом отображения
dataGrid.DataSource = view;
}
}
}
Запустите приложение
и проверьте его функциональность

Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.