Компоненты данных ADO.NET
Добавление класса-оболочки для доступа к полям данных
Для облегчения доступа к данным таблицы Employees учебной базы данных Northwind создадим класс-оболочку с именем EmployeeDetails, который представит нужные нам поля в виде одноименных открытых свойств.
- Через контекстное меню корня Web-дерева создайте каталог с предопределенным именем App_Code
- Вызовите контекстное меню для созданного каталога App_Code и выполните команду Add New Item, чтобы добавить класс C# с именем EmployeeDetails в приложение
-
Заполните созданную оболочкой заготовку класса следующим кодом
using System; public class EmployeeDetails { // Общий конструктор public EmployeeDetails(int employeeID, string firstName, string lastName, string titleOfCourtesy) { this.employeeID = employeeID; this.firstName = firstName; this.lastName = lastName; this.titleOfCourtesy = titleOfCourtesy; } // Конструктор по умолчанию обязателен, // если создали общий конструктор public EmployeeDetails() { } // Добавляем свойства класса private int employeeID; public int EmployeeID { get { return employeeID; } set { employeeID = value; } } private string firstName; public string FirstName { get { return firstName; } set { firstName = value; } } private string lastName; public string LastName { get { return lastName; } set { lastName = value; } } private string titleOfCourtesy; public string TitleOfCourtesy { get { return titleOfCourtesy; } set { titleOfCourtesy = value; } } }
Добавление класса-оболочки для операций с данными
Создадим класс, который в своих методах использует записанные нами ранее хранимые процедуры SQL-запросов к таблице Employees учебной базы данных Northwind. Воспользуемся новым средством частичных классов ( partial ) версии языка C#2.0 и разместим класс с операциями в нескольких файлах, в каждом из которых реализуем один специфический метод.
- Вызовите контекстное меню для созданного каталога App_Code и выполните команду Add New Item, чтобы добавить в приложение класс C# с именем EmployeeDB в файле EmployeeDB.cs
- Из объявлений пространств имен using, автоматически сгенерированных оболочкой, оставьте только using System; и добавьте в заголовок объявления класса ключевое слово partial (частичный), чтобы дать указание компилятору считать одноименные классы в отдельных файлах единым классом (нам так удобнее разместить код отдельных методов)
-
Сделайте в каталоге App_Code шесть копий файла EmployeeDB.cs с именами
- InsertEmployeeDB.cs
- DeleteEmployeeDB.cs
- UpdateEmployeeDB.cs
- GetAllEmployeeDB.cs
- CountEmployeeDB.cs
- GetEmployeeDB.cs
-
Заполните первую часть заготовки класса EmployeeDB в файле EmployeeDB.cs следующим кодом
using System; using System.Web.Configuration; public partial class EmployeeDB { private string connectionString; public EmployeeDB() { // Извлечь из файла web.config строку соединения по умолчанию connectionString = WebConfigurationManager. ConnectionStrings["Northwind"].ConnectionString; } public EmployeeDB(string connectionStringCustom) { // Извлечь из файла web.config другую строку соединения connectionString = WebConfigurationManager. ConnectionStrings[connectionStringCustom].ConnectionString; } }
Обратите внимание, что мы по ходу дела добавляем к коду необходимые пространства имен инструкцией using.
В первой части класса EmployeeDB мы предусмотрели два конструктора для извлечения строки соединения из кофигурационного файла. В других частях класса EmployeeDB мы реализуем методы для выполнения операций с таблицей через соответствующую хранимую процедуру, в каждой части по одному методу. Вот окончательный код этих частей класса, в каждой из которых мы используем безопасный к исключениям подход
using System; using System.Data; using System.Data.SqlClient; public partial class EmployeeDB { public int InsertEmployee(EmployeeDetails emp) { SqlConnection con = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand("InsertEmployee", con); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new SqlParameter("@FirstName", SqlDbType.NVarChar, 10)); cmd.Parameters["@FirstName"].Value = emp.FirstName; cmd.Parameters.Add(new SqlParameter("@LastName", SqlDbType.NVarChar, 20)); cmd.Parameters["@LastName"].Value = emp.LastName; cmd.Parameters.Add(new SqlParameter("@TitleOfCourtesy", SqlDbType.NVarChar, 25)); cmd.Parameters["@TitleOfCourtesy"].Value = emp.TitleOfCourtesy; cmd.Parameters.Add(new SqlParameter("@EmployeeID", SqlDbType.Int, 4)); cmd.Parameters["@EmployeeID"].Direction = ParameterDirection.Output; try { con.Open(); cmd.ExecuteNonQuery(); return (int)cmd.Parameters["@EmployeeID"].Value; } catch { throw new ApplicationException("Ошибка данныx."); } finally { con.Close(); } } }
using System; using System.Data; using System.Data.SqlClient; public partial class EmployeeDB { public void DeleteEmployee(int employeeID) { SqlConnection con = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand("DeleteEmployee", con); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new SqlParameter("@EmployeeID", SqlDbType.Int, 4)); cmd.Parameters["@EmployeeID"].Value = employeeID; try { con.Open(); cmd.ExecuteNonQuery(); } catch { throw new ApplicationException("Ошибка данныx."); } finally { con.Close(); } } }
using System; using System.Data; using System.Data.SqlClient; public partial class EmployeeDB { public void UpdateEmployee(int employeeID, string firstName, string lastName, string titleOfCourtesy) { SqlConnection con = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand("UpdateEmployee", con); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new SqlParameter("@EmployeeID", SqlDbType.Int, 4)); cmd.Parameters["@EmployeeID"].Value = employeeID; cmd.Parameters.Add(new SqlParameter("@FirstName", SqlDbType.NVarChar, 10)); cmd.Parameters["@FirstName"].Value = firstName; cmd.Parameters.Add(new SqlParameter("@LastName", SqlDbType.NVarChar, 20)); cmd.Parameters["@LastName"].Value = lastName; cmd.Parameters.Add(new SqlParameter("@TitleOfCourtesy", SqlDbType.NVarChar, 25)); cmd.Parameters["@TitleOfCourtesy"].Value = titleOfCourtesy; try { con.Open(); cmd.ExecuteNonQuery(); } catch { throw new ApplicationException("Ошибка данныx."); } finally { con.Close(); } } }
using System; using System.Data; using System.Data.SqlClient; using System.Collections.Generic; public partial class EmployeeDB { public List<EmployeeDetails> GetAllEmployees() { SqlConnection con = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand("GetAllEmployees", con); cmd.CommandType = CommandType.StoredProcedure; // Создать коллекцию для всех записей List<EmployeeDetails> employees = new List<EmployeeDetails>(); try { con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { EmployeeDetails emp = new EmployeeDetails( (int)reader["EmployeeID"], (string)reader["FirstName"], (string)reader["LastName"], (string)reader["TitleOfCourtesy"]); employees.Add(emp); } reader.Close(); return employees; } catch { throw new ApplicationException("Ошибка данныx."); } finally { con.Close(); } } }
using System; using System.Data; using System.Data.SqlClient; public partial class EmployeeDB { public int CountEmployees() { SqlConnection con = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand("CountEmployees", con); cmd.CommandType = CommandType.StoredProcedure; try { con.Open(); return (int)cmd.ExecuteScalar(); } catch { throw new ApplicationException("Ошибка данныx."); } finally { con.Close(); } } }
using System; using System.Data; using System.Data.SqlClient; public partial class EmployeeDB { public EmployeeDetails GetEmployee(int employeeID) { SqlConnection con = new SqlConnection(connectionString); SqlCommand cmd = new SqlCommand("GetEmployee", con); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new SqlParameter("@EmployeeID", SqlDbType.Int, 4)); cmd.Parameters["@EmployeeID"].Value = employeeID; try { con.Open(); SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow); // Получить первую строку reader.Read(); EmployeeDetails emp = new EmployeeDetails( (int)reader["EmployeeID"], (string)reader["FirstName"], (string)reader["LastName"], (string)reader["TitleOfCourtesy"]); reader.Close(); return emp; } catch { throw new ApplicationException("Ошибка данныx."); } finally { con.Close(); } } }
Вот такой большой, но зато универсальный получился код нашего компонента, представленного двумя классами-оболочками, один из которых разбит на части в отдельных файлах из соображений удобства программирования. Мы всегда должны помнить, что код нужно делать обозримым, разбивая на отдельные части. Это один из приемов снизить вероятность ошибок, которые, к сожалению, неизбежны в силу несовершенства человеческого ума. Обратите внимание на однообразие структуры методов - эта типизация также снижает ошибки.
Мы видим, что каждый метод сам открывает и закрывает соединение с базой данных. Все тонкости работы с данными скрыты внутри методов. Наша задача теперь, в нужном месте создать экземпляр класса EmployeeDB и правильно вызывать его методы, соблюдая установленный в них интерфейс. Раз создав этот класс, его можно применять многократно по мере необходимости, не заботясь об инкапсулированных в нем тонкостях программирования. Мы же не знаем (да и не хотим знать), как реализованы библиотечные классы .NET Framework. Мы уверены, что они будут работать как надо, если правильно их использовать. Вот это и есть преимущество объектно-ориентированного программирования во всей его красе: обращайся правильно к интерфейсу класса - и все будет работать.