Практикум прикладного программирования на C# в среде VS.NET 2005

Редактирование данных в среде ADO.NET

Показывать лекцию целиком

Файлы к лабораторной работе Вы можете скачать здесь.

В приложении гораздо проще обеспечить пользователю возможность только просмотра данных. Но ни одно реальное приложение не обходится без использования средств редактирования и обновления данных. Для выполнения этой задачи можно применить объект DataSet среды ADO.NET, а также некоторые из его свойств и методов, - чем мы и займемся в данной работе. Для связи с базой данных мы будем использовать объекты DataAdapter, DataSet, DataTable, DataRow.

Некоторые свойства и методы объектов DataAdapter, DataSet
Объект Свойство или метод Описание
DataAdapter Fill() Заполняет объекты DataTable или DataSet
DataAdapter Update() Выполняет обновления в подсоединенных DataTable или DataSet
DataAdapter UpdateCommand Содержит оператор SQL, применяемый для обновления данных
DataAdapter DataAdapter. UpdateCommand. Connection.Close() Принудительно закрывает соединение с базой данных
DataAdapter DataAdapter. UpdateCommand. Connection.Close() Открывает соединение с базой данных
DataSet Tables Представляет коллекцию таблиц, находящихся в наборе данных
DataSet Tables[tableName].Rows Содержит коллекцию строк, принадлежащих к указанной таблице в наборе данных
DataSet AcceptChanges() Подтверждает изменения, передаваемые на сервер

Создание главной формы приложения

  • Создайте новое приложение Windows Application C# с именем DataEdit
  • Добавьте в приложение форму Form0 из приложения DataView лабораторной работы №8. Для этого щелкните правой кнопкой мыши на узле прoекта DataEdit и выпоните команду Add/Add Existing Item (Добавить существующий)
  • В открывшемся окне перейдите в место размещения файла Form0 предыдущего проекта, отметьте его и щелкните по кнопке Open

    Убедитесь, что в панели Solution Explorer проекта DataEdit появился этот файл Form0. Оболочка физически скопировала его из каталога размещения проекта DataView в папку текущего проекта DataEdit.

  • Откройте файл Form1.cs в рещиме редактирования кода. Для этого в панели Solution Explorer щелкните правой кнопкой мыши на имени файла Form1.cs и через контекстное меню выполните команду View Code
  • Через раскрывающийся список в верхней части окна редактора перейдите к функции Main()
  • Удалите весь код, связанный с точкой входа, который идет за блоком Windows Form Designer generated code. Подлежащий удалению код имеет следующий вид
  • /// 
    /// The main entry point for the application.
    /// 
    [STAThread]
    static void Main() 
    {
      Application.Run(new Form1());
    }

    Здесь мы удалили точку входа из сгенерированного мастером файла Form1.cs потому, что она уже существует в добавленном из приложения DataView файле Form0.cs. А две точки входа Большой Билл не перенесет.

  • Откройте файл Form0.cs в режиме View Code и измените пространство имен с DataView на DataEdit как показано ниже
  • //namespace DataView
    namespace DataEdit
  • Закомментируйте в конце файла Form0.cs тело функций-обработчиков событий трех последних кнопок
  • private void button1_Click(object sender, System.EventArgs e)
    {
      Form1 frm = new Form1();
      frm.Show();
    }
      
    private void button2_Click(object sender, System.EventArgs e)
    {
    //  Form2 frm = new Form2();
    //  frm.Show();
    }
      
    private void button3_Click(object sender, System.EventArgs e)
    {
    //  Form3 frm = new Form3();
    //  frm.Show();
    }
      
    private void button4_Click(object sender, System.EventArgs e)
    {
    //  Form4 frm = new Form4();
    //  frm.Show();
    }
  • Запустите приложение и убедитесь, что работает только первая кнопка, которая вызывает пустую форму Form1.
  • Переключите форму Form0 в режим Design и измените содержание текстовых меток, чтобы главная форма выглядела согласно плану выполнения работы так
  • Таким образом, мы создали главную форму, которая будет вызывать наши будующие другие формы. Мы не стали проектировать главную форму с нуля только ради тренировки, хотя традиционный путь в данном случае был бы короче, поскольку настройки формы Form0 очень простые. Но мы ленивые программисты на тупую работу, поэтому таким путем будем часто прикуривать в новом проекте от уже существующего. Не даром создатели оболочки предусмотрели для этих целей соответствующие возможности - видать слыхали о нашем загадочном русском характере.

    Получение из существующей формы нужного интерфейса для новой формы

    Пришла пора приступить к разработке первой формы Form1. Мы здесь не будем сопротивляться нашим национальным особенностям (студент Жук! - можешь посопротивляться, тогда делай Form1 с нуля) и скопируем подходящую для нас форму Form4 из проекта WndAppAdo работы №5.

  • Через главное меню оболочки выполните команду Project/Add Existing Item.
  • Из раскрывающегося списка перейдите в каталог проекта WndAppAdo
  • Выделите файл Form4.cs и щелкните на кнопке Open
  • Обратите внимание, что в панели Solution Explorer нашего текущего проекта сразу появился файл Form4.cs

  • Щелкните в панели Solution Explore правой кнопкой мыши на Form1.cs и удалите его из проекта физически, выполнив команду Delete в контекстном меню
  • Обратите внимание, что если бы выполнить команду Exclude From Project, то файл Form1.cs тоже был бы исключен из проекта, но остался бы существовать физически в каталоге проекта (мы так поступали в работе №5) и его всегда бы можно было вновь вернуть в состав проекта. А здесь мы, предварительно пожалев, что зря только убирали из него точку входа, беспощадно удаляем его навсегда, потому, что сейчас Form4.cs встанет на его место.

  • Вызовите в панели Solution Explorer для файла Form4.cs контекстное меню (или дважды с паузой щелкните по имени) и переименуйте файл в Form1.cs командой Rename
  • Войдите в режим редактирования кода файла Form1.cs (уже нового) и переименуйте пространство имен с WndAppAdo на DataEdit.
  • Вызовите комбинацией клавиш Ctrl-F для файла Form1.cs (нового) окно поиска и замены и замените все вхождения Form4 на Form1
  • Уберите код точки входа (функция Main() )
  • Перейдите для файла Form1.cs в режим Design и, не обращая внимания на посыпавшиеся ошибки, удалите из подвала формы все находящиеся там элементы управления
  • В режиме Code посмотрите в файле Form1.cs, с какими элементами управления связаны старые обработчики, а именно
  • btnLoadList lstCustomers btnEdit btnSave btnCancel
  • Перейдите в режим Design и через панель Properties очистите значения полей соответствующих событий для каждого элемента, чтобы отвязать элементы интерфейса от обработчиков, например
  • Было Стало
  • Перейдите в режим Code и безжалостно удалите код, представляющий все обработчики и вспомогательные методы, введенные нами при выполнении лабораторной работы №5 (чего жалеть, новый накопируете). Этот весь код находится ниже блока Windows Form Designer generated code. Будтьте осторожны, чтобы случайно не удалить закрывающие фигурные скобки класса и пространства имен.
  • Постройте проект, чтобы проверить отсутствие ошибок, откройте через верхнюю кнопку главной формы нашу многотрудную форму Form1 и на экране появится кристально чистый нужный нам пользовательский интерфейс
  • Ну что, удалять ведь легче, чем ваять! (Студент Жук, где Вы там вдали еще плететесь. А мы с Панасюком (и с русской ленью) уже даже сальцо порезали со шкварками! Да уже и Delphi лучком присыпали, и Кузмича распечатали. Вас поджидаем, да добра наживаем). Хотя при чем здесь национальный характер, когда весь мир так работает: возьмет чужое, подтешет, подбелит, подкрасит и спихнет как свое - и... на Богамы. Возмите любой мало-мальски стоящий сайт, построенный нашими умельцами для тупых коммерсантов, перейдите в режим просмотра кода - сплошная английская речь и один и тот же код у разных разработчиков. Передирают друг у друга беспощадно, даже чужие комментарии не удосужатся убрать.

    А теперь, по порядочку будем строить код поддержки нашего интерфейса для формы Form1, реализующий редактирование данных и обновление изменений средствами ADO.NET. При этом забудем про компоненты вкладки Data, а воспользуемся только возможностями классов библиотеки Framework BCL. Оперировать вновь будем с данными учебной библиотеки Northwind ( можно взять в каталоге Data лаб.4 или лаб.№5 ).

    Редактирование данных средствами DataSet

  • В конце класса Form1 в режиме Code объявите ссылочные переменные как показано ниже, чтобы они были видны во всех будущих методах этого класса
  • namespace DataEdit
    {
      public class Form1 : System.Windows.Forms.Form
      {
    ..........................................................    
        // Windows Form Designer generated code   
           
        // Переменные-члены класса Form1
        System.Data.DataSet mdsCustIndiv = 
          new System.Data.DataSet();// Объявили и создали набор данных
        System.Data.OleDb.OleDbDataAdapter modaCustIndiv;// Только объявили поставщика
        System.Data.DataRow mdrCustIndiv;// Ссылочная переменная
      }
    }

    Задача современного программиста преимущественно сводится к последовательному наращиванию своих классов, которые всю базовую функциональность наследуют от библиотечных классов, хорошо продуманных Большим Биллом и сотоварищами. Мы постепенно компонуем наше приложение из объектов, произведенных от этих классов. Классы здесь выступают как проекты, выполненные Большим Архитектором, а мы должны хорошо разбираться в этих проектах и строить по ним так, чтобы здание стояло и крыша не съехала (ни наша, ни программы). А с нуля ничего стоящего, что можно-бы было продать, сейчас уже не сделаешь - жизни не хватит.

  • Перейдите в режим Design в форме Form1, создайте обработчик события Click для кнопки btnLoadList и заполните его кодом так
  • private void btnLoadList_Click(object sender, System.EventArgs e)
    {
      // Выделить код загрузки в отдельную функцию для возможности
      // многократного использования в этом и других классах
      LoadList();// Наша функция, см.ниже
    }

    Здесь мы искусственно выделили код обработчика в вызов отдельной функции, чтобы иметь возможность компактно обратиться к ней из другого места программы в случае необходимости.

    // Наша функция!!!
    // Для загрузки и заполнения всего
    private void LoadList()
    {
      // Локальные переменные
      System.Data.OleDb.OleDbDataAdapter odaCustList;
      System.Data.DataTable dtCustList = new DataTable();
      System.String strSQL;
      
      // Сформировать строку SQL-запроса закачки в DataTable
      // только полей CustomerID и CompanyName по значению TextBox
      strSQL = "SELECT CustomerID, CompanyName "
        + "FROM Customers WHERE CustomerID LIKE '"
        + txtCustLimit.Text
        + "%'";
      
      // Создать безопасный к исключениям код
      try
      {
        // Создать экземпляр поставщика данных с учетом 
        // значения TextBox, затем заполнить таблицу данных
        odaCustList = new System.Data.OleDb.OleDbDataAdapter(
          strSQL,
          BuildCnnStr("localhost", "Northwind")); // Наша функция, см.ниже
        odaCustList.Fill(dtCustList);
      
        // Связать ListBox с DataTable для отображения данных
        lstCustomers.DataSource = dtCustList;
        lstCustomers.DisplayMember = "CompanyName";
        lstCustomers.ValueMember = "CustomerID";
      
        // Загрузить значения всех полей из поставщика в DataRow,
        // соответствующих строке выделенного элемента списка,
        // затем заполнить значениями все боковые текстовые боксы
        LoadIndividual(); // Наша функция, см.ниже
      }
      catch(System.Data.OleDb.OleDbException oexpData)
      {
        // Объект исключения специализируется на базах данных!!!
        System.Windows.Forms.MessageBox.Show(oexpData.Message);
        return;
      }
    }

    В отдельную функцию для удобства мы выделили формирование строки соединения поставщика с сервером и базой данных

    // Наша функция!!! 
    // Формирует и возвращает строку параметров для создания
    //  соединения поставщика с сервером и базой данных
    private string BuildCnnStr(string strServer, string strDatabase)
    {
      string strTemp = "Provider=SQLOleDB; Data Source="
        + strServer + ";";
      strTemp += "Initial Catalog=" + strDatabase + ";";
      strTemp += "Integrated Security=SSPI";
      return strTemp;
    }

    После того, как поставщик данных будет заполнен, соединение автоматически разорвется и под личиной базы данных для пользователя будет скрываться теперь поставщик данных, хранящий закаченные в момент соединения данные уже на локальном компьютере пользователя. А глупый пользователь будет думать, что все еще остается на связи с базой и что меняет в текущий момент данные чуть ли не прямо на сервере Пентагона.

    После того, как данные от поставщика через таблицу данных (через директор магазин, через товаровед, через завсклад) поступили в список в количестве двух полей и в списке оказалась выделенной текущая запись таблицы, сразу же нужно выбрать значения остальных полей текущей записи таблицы и через набор данных (через завсклад, через товаровед, через директор магазин) передать их в текстовые поля для просмотра и редактирования. Код, реализующий эту задачу, будет таким

    // Наша функция!!!
    // Загружает в набор данных значения полей выделенной в списке записи
    // для дальнейшего отображения на экране в текстовых полях TextBox
    private void LoadIndividual()
    {
      // Очистить набор данных
      mdsCustIndiv.Clear();
      
      // Если строка списка выделена, выбираем данные
      // для заполнения соответствующими значениями текстовых полей
      if(lstCustomers.SelectedIndex == -1) return;
      
      // Создать безопасный к исключениям код
      // для выборки индивидуальных данных
      try
      {
        // Формируем строку SQL-запроса
        System.String strSQL = "SELECT * FROM Customers WHERE CustomerID = '"
          + lstCustomers.SelectedValue // Выбрать все поля записи, 
          + "'"; // значение ключа которой равно выделенному в списке
      
        // Создаем поставщик данных для нужной базы
        modaCustIndiv = new System.Data.OleDb.OleDbDataAdapter(
          strSQL,
          BuildCnnStr("localhost", "Northwind")); // Наша функция, см.выше
      
        // Теперь вместо таблицы наливаем 
        // из поставщика данные в набор данных
        modaCustIndiv.Fill(mdsCustIndiv, "Customers");
      
        // Получить отдельную запись из коллекции строк
        mdrCustIndiv = mdsCustIndiv.Tables["Customers"].Rows[0];
      }
      catch(System.Data.OleDb.OleDbException oexpData)
      {
        System.Windows.Forms.MessageBox.Show(
          "Ошибка в загрузке индивидуальных данных: " + 
          oexpData.Message);
        return;
      }
      
      // Заполнить текстовые поля формы Form1
      // извлеченными значениями данных
      try
      {
        FillTextBox(); // Наша функция, см.ниже
      }
      catch(System.Exception oexp)
      {
        // Если обработчика исключений нет,
        // то все ошибки игнорируются
      }
    }
      
    // Наша функция!!!
    // Заполняет текстовые элементы TextBox 
    // значениями полей выбранной пользователем записи
    private void FillTextBox()
    {
      txtCustomerID.Text = mdrCustIndiv["CustomerID"].ToString();
      txtCompanyName.Text = mdrCustIndiv["CompanyName"].ToString();
      txtContactName.Text = mdrCustIndiv["ContactName"].ToString();
      txtContactTitle.Text = mdrCustIndiv["ContactTitle"].ToString();
      txtAddress.Text = mdrCustIndiv["Address"].ToString();
      
      // Учебное извращение с учетом того, что имена элементов TextBox
      // у нас совпадают с именами полей таблицы Customers + префикс "txt".
      // Хотя от такой сильной связи лучше отказываться - поменяли
      // поля, и все пошло кувырком, и код получается более запутанный
      string tmp = txtCity.Name.ToString().Substring(3);
      txtCity.Text = mdrCustIndiv[tmp].ToString();
      
      tmp = txtRegion.Name.ToString().Substring(3);
      txtRegion.Text = mdrCustIndiv[tmp].ToString();
      
      tmp = txtPostalCode.Name.ToString().Substring(3);
      txtPostalCode.Text = mdrCustIndiv[tmp].ToString();
      
      tmp = txtCountry.Name.ToString().Substring(3);
      txtCountry.Text = mdrCustIndiv[tmp].ToString();
      
      tmp = txtPhone.Name.ToString().Substring(3);
      txtPhone.Text = mdrCustIndiv[tmp].ToString();
      
      tmp = txtFax.Name.ToString().Substring(3);
      txtFax.Text = mdrCustIndiv[tmp].ToString();
    }
  • Перейдите в режим Design формы Form1, создайте обработчик события SelectedIndexChanged для списка ListBox и заполните его как показано ниже
  • // Наша функция!!!
    // Меняет содержимое элемента набора данных DataSet
    // при каждом выборе пользователем значения списка
    private void lstCustomers_SelectedIndexChanged(object sender, System.EventArgs e)
    {
      LoadIndividual();
    }
  • Постройте форму, которая внешне должна выглядеть примерно так
  • Убедитесь, что заложенная нами функциональность формы полностью поддерживается.

    Добавление и удаление строк в наборе данных средствами среды ADO.NET

    Здесь мы опять будем работать без использования элементов управления вкладки Data, а только с классами, соединяясь с элементами управления только на этапе выполнения. И будем реализовывать возможности классов для поддержки задачи редактирования данных.

    В качестве основы загрузки данных в этом упражнении мы оставим код предыдущей формы Form1, создав ее копию, и разовьем возможности, добавив средства редактирования с помощью соответствующих функциональностей классов.

  • В панели Solution Explorer проекта щелкните правой кнопкой мыши на узле Form1.cs и выполните команду Copy контекстного меню
  • Щелкните правой кнопкой мыши на узле самого проекта в дереве панели Solution Explorer и выполните команду Paste
  • Переименуйте созданную копию формы Form1 в файл Form2.cs
  • Щелкните правой кнопкой мыши на новом узле Form2.cs и выполните команду меню View Cide
  • В редакторе кода файла Form2.cs вызовите комбинацией клавиш Ctrl-F окно поиска и замены и выполните в коде замену всех вхождений Form1 на Form2
  • Откройте файл Form0.cs в режиме кода и раскомментируйте заблокированный нами ранее код открытия формы Form2 в обработчике второй кнопки
  • private void button2_Click(object sender, System.EventArgs e)
    {
      Form2 frm = new Form2();
      frm.Show();
    }
  • Запустите приложение, откройте форму Form2 и убедитесь, что она работоспособна и ее функциональность соответствует форме Form1 (эта ее копия и мы пока еще ничего в ней не резали и не клеили - ни селедку, ни Большого Билла).
  • Приступим к наращиванию кода в Form2 для добавления возможностей редактирования

  • Введите в класс Form2 объявление булевой переменной mblnAdd с начальной инициализацией false. Эта переменная будет играть роль флага для включения и отключения режима добавления новой записи в базу данных.
  • ..........................................    
    // Windows Form Designer generated code    
      
    // Переменные-члены класса Form2
    System.Data.DataSet mdsCustIndiv = 
      new System.Data.DataSet();// Объявили и создали набор данных
    System.Data.OleDb.OleDbDataAdapter modaCustIndiv;// Только объявили поставщика
    System.Data.DataRow mdrCustIndiv;// Ссылочная переменная
    bool mblnAdd = false;// Флаг режима добавления записи
    ..........................................
  • Поместите в форму две новых кнопки для добавления и удаления записей со следующими значениями свойств
  • Свойства кнопок добавления и удаления записей
    Объект Свойство Значение
    Button Name btnAdd
      Text Add
    Button Name btnDelete
      Text Delete

    Кнопка Add будет включать режим добавления новой записи, которая будет добавлена в базу данных по нажатию кнопки Save.

  • В режиме Design выполните команду Format/Lock Controls так, чтобы сначала разомкнуть все элементы формы, а затем вновь замкнуть, дабы подсадить в эту компанию и новые кнопки.
  • В режиме Design добавьте в форму Form2 обработчик события Load
  • Наполните код обработчика так
  • private void Form2_Load(object sender, System.EventArgs e)
    {
      AccessButtons(false); // Закрыть кнопки управления
    }
      
    //*****************************************************************
    // Наша функция!!!
    // Регулирует доступность кнопок
    // в соответствующие поля набора данных через ссылку на строку
    private void AccessButtons(bool bEnable)
    {
      // Разрешить или запретить применение соответствующих кнопок
      btnEdit.Enabled = bEnable;
      btnAdd.Enabled = bEnable;
      btnDelete.Enabled = bEnable;
      btnCancel.Enabled = bEnable;
      btnSave.Enabled = bEnable;
    }

    Этот обработчик нужен для отключения функциональности кнопок, пока данные еще не загружены.

  • Создайте обработчик события Click для кнопки Add. В этом обработчике прежде всего нужно поднять флаг режима добавления новой записи в базу данных. Затем необходимо очистить текстовые поля и снять с них режим ReadOnly. Код обработчика приведен ниже
  • //*****************************************************************
    //*****************************************************************
    // Дополнение класса возможностями редактирования данных
    //*****************************************************************
    //*****************************************************************
      
    // Обработчик кнопки Add
    private void btnAdd_Click(object sender, System.EventArgs e)
    {
      mblnAdd = true; // Включен режим добавления записи
      ClearEditing();
      ActivateEditing(true);
    }
      
    //*****************************************************************
    // Функция очистки текстовых полей
    private void ClearEditing()
    {
      txtCustomerID.Text = 
      txtCompanyName.Text = 
      txtContactName.Text = 
      txtContactTitle.Text = 
      txtAddress.Text = 
      txtCity.Text = 
      txtRegion.Text = 
      txtPostalCode.Text = 
      txtCountry.Text = 
      txtPhone.Text = 
      txtFax.Text = "";    
    }
      
    //*****************************************************************
    // Функция изменения свойств текстовых полей
    private void ActivateEditing(bool bEnable)
    {
      // Только для чтения
      txtCustomerID.ReadOnly = 
      txtCompanyName.ReadOnly = 
      txtContactName.ReadOnly = 
      txtContactTitle.ReadOnly = 
      txtAddress.ReadOnly = 
      txtCity.ReadOnly = 
      txtRegion.ReadOnly = 
      txtPostalCode.ReadOnly = 
      txtCountry.ReadOnly = 
      txtPhone.ReadOnly = 
      txtFax.ReadOnly = !bEnable;
      
      // Стиль текстовых полей
      if(bEnable)
      {
        txtCustomerID.BorderStyle = 
        txtCompanyName.BorderStyle = 
        txtContactName.BorderStyle = 
        txtContactTitle.BorderStyle = 
        txtAddress.BorderStyle = 
        txtCity.BorderStyle = 
        txtRegion.BorderStyle = 
        txtPostalCode.BorderStyle = 
        txtCountry.BorderStyle = 
        txtPhone.BorderStyle = 
        txtFax.BorderStyle = BorderStyle.Fixed3D;
      }
      else
      {
        txtCustomerID.BorderStyle = 
        txtCompanyName.BorderStyle = 
        txtContactName.BorderStyle = 
        txtContactTitle.BorderStyle = 
        txtAddress.BorderStyle = 
        txtCity.BorderStyle = 
        txtRegion.BorderStyle = 
        txtPostalCode.BorderStyle = 
        txtCountry.BorderStyle = 
        txtPhone.BorderStyle = 
        txtFax.BorderStyle = BorderStyle.FixedSingle;
      }
      
      // Разрешить или запретить применение соответствующих кнопок
      btnEdit.Enabled = !bEnable;
      btnDelete.Enabled = !bEnable;
      btnCancel.Enabled = bEnable;
      btnSave.Enabled = bEnable;
      
      // Установить фокус ввода на первое текстовое поле CustomerID
      if(bEnable)
        txtCustomerID.Focus();
    }

    Теперь нужно добавить обработчик события Click кнопки Save. В коде обработчика нужно проверить флаг режима добавления и принять решение, обновлять ли базу с добавлением новой записи или просто заменить значения текущих данных, считая, что проводилось просто редактирование.

  • Создайте обработчик события Click кнопки Save, которая у нас на форме присутствует, и заполните его следующим кодом
  • //*****************************************************************
    // Обработчик кнопки Save
    private void btnSave_Click(object sender, System.EventArgs e)
    {
      ActivateEditing(false);
      SaveRecord();
      mblnAdd = false;
    }
      
    //*****************************************************************
    // Сохранить изменения в базе данных
    private void SaveRecord()
    {
      if(mblnAdd)
      {
        // Создать новую строку в наборе данных
        // и сохранить ссылку на нее в ссылочной переменной
        mdrCustIndiv = mdsCustIndiv.Tables["Customers"].NewRow();
      }
      
      // Перекачать значения текстовых полей в строку набора данных
      mdrCustIndiv.BeginEdit();  // Открыть разрешение
      TextBoxToDataSet();      // Перекачать - наша функция!!!
      mdrCustIndiv.EndEdit();    // Закрыть разрешение
      
      // Использовать защищенный режим
      try
      {
        // Если режим добавления, то расширить набор 
        // данных, включив туда сформированную строку
        if(mblnAdd)
          mdsCustIndiv.Tables["Customers"].Rows.Add(mdrCustIndiv);
      
        // Создать экземпляр построителя SQL-команды
        System.Data.OleDb.OleDbCommandBuilder ocbCustIndiv = 
          new System.Data.OleDb.OleDbCommandBuilder(modaCustIndiv);
      
        // Настроить поставщик данных на действия
        // с помощью экземпляра построителя команды
        if(mblnAdd)
        {
          // Обеспечить вставку новой записи
          modaCustIndiv.InsertCommand = ocbCustIndiv.GetInsertCommand();
        }
        else
        {
          // Обеспечить обновление изменений существующей записи
          modaCustIndiv.UpdateCommand = ocbCustIndiv.GetUpdateCommand();
        }
      
        // Выполнить автоматически сгенерированную команду SQL
        modaCustIndiv.Update(mdsCustIndiv, "Customers");
        mdsCustIndiv.Tables["Customers"].AcceptChanges();
      
        // Отключиться от базы данных
        if(mblnAdd)
        {
          modaCustIndiv.InsertCommand.Connection.Close();
          LoadList();
        }
        else
          modaCustIndiv.UpdateCommand.Connection.Close();
      }
      catch(System.Data.OleDb.OleDbException excData)
      {
        System.Windows.Forms.MessageBox.Show(
          "Произошла ошибка: " + excData.Message);
      }
    }
      
    //*****************************************************************
    // Наша функция!!!
    // перекачивает текстовые поля элементов TextBox 
    // в соответствующие поля набора данных через ссылку на строку
    private void TextBoxToDataSet()
    {
      mdrCustIndiv["CustomerID"] = txtCustomerID.Text;
      mdrCustIndiv["CompanyName"] = txtCompanyName.Text;
      mdrCustIndiv["ContactName"] = txtContactName.Text;
      mdrCustIndiv["ContactTitle"] = txtContactTitle.Text;
      mdrCustIndiv["Address"] = txtAddress.Text;
      mdrCustIndiv["City"] = txtCity.Text;
      mdrCustIndiv["Region"] = txtRegion.Text;
      mdrCustIndiv["PostalCode"] = txtPostalCode.Text;
      mdrCustIndiv["Country"] = txtCountry.Text;
      mdrCustIndiv["Phone"] = txtPhone.Text;
      mdrCustIndiv["Fax"] = txtFax.Text;
    }
  • Создайте обработчик события Click кнопки Cancel, которая у нас на форме присутствует, и заполните его следующим кодом
  • //*****************************************************************
    private void btnCancel_Click(object sender, System.EventArgs e)
    {
      mblnAdd = false;  // Сбросить флаг режима добавления
      LoadIndividual();  // Повторить заполнение TextBox
      ActivateEditing(false);  // Перевести поля в режим "Только для чтения"
    }
  • Создайте обработчик события Click кнопки Delete и наполните его так
  • //*****************************************************************
    private void btnDelete_Click(object sender, System.EventArgs e)
    {
      // Использовать защищенный режим
      try
      {
        // Удалить запись из объекта строки данных
        mdrCustIndiv.Delete();
      
        // Создать экземпляр построителя SQL-команды
        System.Data.OleDb.OleDbCommandBuilder ocbCustIndiv = 
          new System.Data.OleDb.OleDbCommandBuilder(modaCustIndiv);
      
        // Настроить поставщик данных на автоматическое удаление
        modaCustIndiv.DeleteCommand = ocbCustIndiv.GetDeleteCommand();
      
        // Выполнить автоматически сгенерированную команду SQL
        modaCustIndiv.Update(mdsCustIndiv, "Customers");
        mdsCustIndiv.Tables["Customers"].AcceptChanges();
      
        // Отключиться от базы данных
        modaCustIndiv.DeleteCommand.Connection.Close();
      }
      catch(System.Data.OleDb.OleDbException excData)
      {
        System.Windows.Forms.MessageBox.Show(
          "Произошла ошибка: " + excData.Message);
      }
      
      // Обновить экран свежими данными
      LoadList();
      
      // Установить режим текстовых полей "Только для чтения"
      ActivateEditing(false);
    }
  • Создайте обработчик события Click кнопки Edit и наполните его так
  • //*****************************************************************
    // Обработчик события Click кнопки Edit
    private void btnEdit_Click(object sender, System.EventArgs e)
    {
      // Разрешить редактирование данных в кэше
      ActivateEditing(true);
    }
  • Постройте приложение и проверьте его работоспособность. Помните, что максимальный размер поля CustomerID равен 5. Это поле ключевое, поэтому его заполнение обязательно. Несоблюдение этих условий при испытании формы приведет к генерации ошибки базой данных.
  • Внешний вид формы Form2 будет примерно таким

    Выполнение параметризованных хранимых процедур в среде ADO.NET

    Важным преимуществом хранимых процедур является возможность передавать им параметры и определять таким образом конкретные критерии отбора данных. В этом упражнении показано, как обеспечить формирование параметров в объекте System.Data.OleDb.OleDbCommand для их передачи в базу данных SQL Server на основе средств C# и ADO.NET.

    Для решения задачи мы будем использовать свойства и методы следующих объектов:

  • Перечислим те из них, которые нам понадобятся для выполнения данного упражнения

    Некоторые классы и их характеристики
    Класс Свойство или метод Описание
    OleDbConnection ConnectionString Содержит применяемую строку подключения
      Open() Открывает соединение, которое используется объектом OleDbCommand
    OleDbCommand CommandText Указывает применяемый оператор SQL. Может содержать оператор SQL или имена таких объектов, как таблицы или хранимые процедуры
      Connection Использует объект OleDbConnection
      CommandType

    Указывает тип команды, которая должна быть выполнена на сервере. В качестве значения этого свойства может быть указано одно из значений перечисления System.Data.CommandType:

  • System.Data.CommandType.StoredProcedure
  • System.Data.CommandType.TableDirect
  • System.Data.CommandType.Text
  •   Parameters Параметры, передаваемые хранимой процедуре
      ExecuteReader() Создает объект System.Data.OleDb.OleDbDataReader с данными, указанными в объекте-поставщике OleDbCommand
    OleDbDataReader Read() Считывает следующую запись в объекте OleDbDataReader, а также проверяет, достигнут ли конец читаемых данных
      GetString() Возвращает значение указанного столбца текущей записи как строку
      GetInt32() Получает значение указанного столбца текущей записи как 32-разрядное целое число со знаком

    Выполнение упражнения

  • Добавьте в проект новую форму, выполнив команду меню оболочки Project/Add Windows Form
  • Оставьте имя формы по умолчанию Form3
  • Добавьте вверху исходного кода сгенерированного файла Form3.cs пространства имен в режиме View Code из библиотеки system.data.dll
  • using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    using System.Data.OleDb;
      
    namespace DataEdit
    {
      public class Form3 : System.Windows.Forms.Form
    .................................................
    }
  • Проверьте, что библиотека system.data.dll с нужными пространствами имен подключена к проекту
  • Если библиотека отсутствует в ссылках проекта, то подключите ее через контекстное меню узла References командой Add Reference

  • В режиме Design поместите на форму из вкладки Windows Forms панели Toolbox элементы управления пользовательского интерфейса со значениями, указанными в таблице
  • Элементы управления формы Form3 и их свойства
    Объект Свойство Значение
    Label Text CustomerID:
      TextAlign MiddleLeft
    TextBox Name txtCustID
      Text ALFKI
    Button Name btnView
      Text View
    TextBox Name txtResults
      MultiLine True
      Dock Bottom
      Text Пусто
      ScrollBars Vertical
  • Расположите элементы управления пользовательского интерфейса так
  • Через меню Format замкните элементы командой Lock Controls, чтобы случайно не изменить интерфейс
  • Двойным щелчком на кнопке View создайте обработчик события Click и заполните его кодом, приведенным ниже.
  • В коде обработчика создается объект команды с учетом информации о конкретном соединении. В базу данных передается имя хранимой процедуры ( StoredProcedure ) с указанием типа команды как значения структуры System.Data.CommandType.StoredProcedure. Затем формируются параметры и создается объект типа System.Data.OleDb.OleDbDataReader. На последнем этапе выполняется обработка данных в цикле и размещение их в строках TextBox.

    private void btnView_Click(object sender, System.EventArgs e)
    {
      // Создаем объект соединения с базой данных
      OleDbConnection ocnn = new OleDbConnection(
        this.BuildCnnStr("localhost", "Northwind"));
      // Создаем объект команд
      OleDbCommand ocmdCustHist = new OleDbCommand("CustOrderHist", ocnn);
      // Объявляем ссылку на будущий объект чтения данных
      OleDbDataReader odrCustHist;
      
      // Защищенный режим выполнения
      try
      {
        // Указать тип команды как "хранимая процедура"
        ocmdCustHist.CommandType = CommandType.StoredProcedure;
        // Задать параметры команды
        ocmdCustHist.Parameters.Add("@CustomerID", this.txtCustID.Text);
        // Открыть объект соединения
        ocnn.Open();
        // Создать объект чтения данных
        odrCustHist = ocmdCustHist.ExecuteReader(CommandBehavior.SequentialAccess);
        // Обработать в цикле загруженные данные
        // и создать строку результатов
        this.txtResults.Clear();
        while(odrCustHist.Read())
        {
          this.txtResults.AppendText(odrCustHist.GetString(0)
            + ", " + odrCustHist.GetInt32(1).ToString() + "\r\n");
        }
      }
      catch(System.Exception excpData)
      {
        MessageBox.Show("Произошла ошибка: " + excpData.Message);
      }
    }
      
    // Наша функция!!! 
    // Формирует и возвращает строку параметров для создания
    //  соединения поставщика с сервером и базой данных
    private string BuildCnnStr(string strServer, string strDatabase)
    {
      string strTemp = "Provider=SQLOleDB; Data Source="
        + strServer + ";";
      strTemp += "Initial Catalog=" + strDatabase + ";";
      strTemp += "Integrated Security=SSPI";
      return strTemp;
    }
  • Откройте файл Form0.cs в режиме кода и раскомментируйте заблокированный нами ранее код открытия формы Form3 в обработчике второй кнопки
  • private void button3_Click(object sender, System.EventArgs e)
    {
      Form3 frm = new Form3();
      frm.Show();
    }
  • Запустите приложение, откройте форму Form3
  • После указания идентификатора заказчика CustomerID и щелчка на кнопке View заполняется расположенное ниже текстовое поле и в нем отображается информация о заказах выбранного заказчика. Результирующий экран будет такой

    Динамическое создание и выполнение пакетных заданий по обновлению данных в среде ADO.NET

    Иногда в приложениях для баз данных во время выполнения программы приходится создавать и вызывать хранимые процедуры, которые не существуют в базе данных. В этом упражнении показано, как это делать. Мы будем использовать объект System.Data.OleDb.OleDbCommand, в котором свойству CommandText присваивается значение из текстового поля. В текстовом поле мы зададим SQL-оператор

    UPDATE Employees SET City = 'Redmond' WHERE City = 'Seattle'

    При нажатии кнопки Выполнить программа откроет таблицу Employees в базе данных Northwind и заменит в поле City все значения Seattle на новые значения Redmond. При этом функция выполнения команды вернет количество записей, в которых выполнены замены.

  • Создайте новую форму с именем Form4
  • Добавьте вверху исходного кода сгенерированного файла Form4.cs пространства имен в режиме View Code
  • using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    using System.Data.OleDb;
      
    namespace DataEdit
    {
      public class Form4 : System.Windows.Forms.Form
    .................................................
    }
  • Поместите на форму элементы управления со значениями свойств согласно таблице
  • Элементы управления формы Form4 и их свойства
    Объект Свойство Значение
    Label Text Введите и выполните команду обновления данных:
      TextAlign MiddleCenter
    TextBox Name txtSQL
      Text UPDATE Employees SET City = 'Redmond' WHERE City = 'Seattle'
      MultiLine True
    Label Text Найденно записей:
    TextBox Name txtRecsAffected
      ReadOnly True
      Text Пусто
      BorderStyle FixedSingle
    Button Name btnExecute
      Text Выполнить
  • Дизайн формы должен быть таким
  • Через меню Format замкните элементы командой Lock Controls, чтобы случайно не изменить интерфейс
  • Двойным щелчком на кнопке Выполнить создайте обработчик события Click и заполните его кодом, приведенным ниже.
  • В коде после порождения экземпляра объекта команды свойству CommandText этого объекта присваивается строковое значение текстового поля txtSQL. Тип команды устанавливается как CommandType.Text. Затем открывается соединение. Выполнение команды осуществляется методом System.Data.OleDb.OleDbCommand.ExecuteNonQuery().

    Возвращаемое значение, представляющее собой количество записей, измененных по команде, помещается в текстовое поле txtRecsAffected.

    private void btnExecute_Click(object sender, System.EventArgs e)
    {
      // Создаем объект соединения с базой данных
      OleDbConnection ocnn = new OleDbConnection(
        this.BuildCnnStr("localhost", "Northwind"));
      // Создаем объект команд
      OleDbCommand ocmdPhoneUp = new OleDbCommand(this.txtSQL.Text, ocnn);
      
      // Защищенный режим выполнения
      try
      {
        // Указать тип команды как "текст"
        ocmdPhoneUp.CommandType = CommandType.Text;
        // Открыть объект соединения
        ocnn.Open();
        // Выполняем команду без запроса на выборку данных
        this.txtRecsAffected.Text = 
          ocmdPhoneUp.ExecuteNonQuery().ToString();
      }
      catch(System.Exception excpData)
      {
        MessageBox.Show("Произошла ошибка: " + excpData.Message);
      }
    }
      
    // Наша функция!!! 
    // Формирует и возвращает строку параметров для создания
    //  соединения поставщика с сервером и базой данных
    private string BuildCnnStr(string strServer, string strDatabase)
    {
      string strTemp = "Provider=SQLOleDB; Data Source="
        + strServer + ";";
      strTemp += "Initial Catalog=" + strDatabase + ";";
      strTemp += "Integrated Security=SSPI";
      return strTemp;
    }
  • Откройте файл Form0.cs в режиме кода и раскомментируйте заблокированный нами ранее код открытия формы Form4 в обработчике второй кнопки
  • private void button4_Click(object sender, System.EventArgs e)
    {
      Form4 frm = new Form4();
      frm.Show();
    }
  • Запустите приложение, откройте форму Form4 и выполните с ее помощью замену данных в таблице Employees
  • Внешний вид выполненных вариантов будет таким

    Вернуться к учебному плану