Доступ к данным |Доступ к данным с помощью ADO.NET | 7. Работа с данными в таблицах ADO.NET Дальше »
7. Работа с данными в таблицах ADO.NET
7.1. Добавление данных в таблицу
7.2. Состояния строки
7.3. Removing или Deleting DataRow из таблицы
7.4. Работа с данными в таблице
7.5. Добавление и чтение информации об ошибках строки
7.6. Принятие или отклонение изменений строк
7.7. Работа с событиями DataTable
7.8. Фильтрация и сортировка строк в таблице
7.9. Optimistic Concurrency
7.10. Исполнение транзакций

7. Работа с данными в таблицах ADO.NET

7.1. Добавление данных в таблицу

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

[VB]
Dim iCounter as integer
  Dim workRow as DataRow
 
  For i = 0 to 9
   workRow = worktable.NewRow()
   workRow("CustID") = iCounter
   workRow("CustomerNameLast") = "CustName" & i.ToString()
   worktable.Rows.Add(workRow)
  Next

[C#]
 DataRow workRow = null;
 for (int i = 0; i <= 9; i++)
 {
  workRow = workTable.NewRow();
  workRow[0] = i;
  workRow[1] = "CustName" + i.ToString();
  workTable.Rows.Add(workRow);
}

В этом коде объявляется новая переменная с типом DataRow. Новый объект DataRow возвращается при вызове метода NewRow. DataTable создаёт объект DataRow, основанный на структуре таблицы (как это определено в ColumnsCollection).

[VB]
Dim workRow As DataRow
workRow = workTable.NewRow()

[C#]
Data.DataRow workRow;
workRow = workTable.NewRow();

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

[VB]
workRow("CustID") = "CustName" & i
workRow(1) = "CustName" & i

[C#]
workRow["CustID"] = "CustName" + i.ToString();
workRow[1] = "CustName" + i.ToString();

После того, как строка заполнена значениями, её можно добавить в RowsCollection таблицы, используя метод Add.

[VB]
workTable.Rows.Add(workRow)

[C#]
workTable.Rows.Add(workRow);

В дополнение к добавлению строк этим путём, мы можем также добавлять новую строку, получая значения из массива (типизированного, как объект) и используя метод Add.

[VB]
workTable.Rows.Add(new Object() {1, "CustName1"})

[C#]
workTable.Rows.Add(new Object[] {1, "CustName1"});

Этот способ просто создаёт новую строку внутри таблицы и устанавливает для значения столбца, значение из объекта массив. Обратите внимание, что последовательность значений в массиве согласована с последовательностью значений столбца в таблице.

7.2. Состояния строки

Каждая DataRow имеет свойство RowState, которое определяет её состояние.
В приведённом выше примере, RowState недавно созданной строки установится в состояние Detached. Однако, сразу после того, как строка будет добавлена в RowsCollection методом Add, свойство RowState установится в New.
Приведённая ниже таблица содержит возможные состояния, которые строка может принимать.

RowState Описание
Unchanged Никакие изменения не делались, начиная с последнего запроса AcceptChanges
New Строка была добавлена в таблицу, но AcceptChanges не вызывался
Modified Некоторый элемент строки был изменены.
Deleted Строка была удалена из таблицы с помощью метода Delete
Detached Либо строка была удалена, но AcceptChanges не вызывался, либо строка была создана, но не добавлена к таблице.

7.3. Removing или Deleting DataRow из таблицы

При выполнении операций удаления одной или более строк из таблицы, обратите внимание на то, что removing не то же, что deleting. Важность этого различия будет рассмотрена позже в обсуждении способов сохранения значений таблицы в источнике данных.
Чтобы удалить строку из таблицы, вызовите метод Remove из RowsCollection таблицы (доступный через свойство Rows объекта DataTable), воспользовавшись индексом или непосредственно экземпляром строки.

[VB]
workTable.Rows.Remove(3)

[C#]
workTable.Rows.Remove(3);

ИЛИ

[VB]
workTable.Rows.Remove(workTable.Rows(3))

[C#]
workTable.Rows.Remove(workTable.Rows[3]);

После вызова этого метода, указанная строка будет удалена из совокупности строк. В отличии от этого, Вы можете вызвать метод Delete для DataRow, чтобы изменить её состояние на Deleted:

[VB]
workTable.Rows(3).Delete

[C#]
workTable.Rows[3].Delete

Отметка строки, как удаленной, говорит о том, что когда будет вызван метод AcceptChanges объекта DataTable, строка будет удален из таблицы. Это приведёт к изменению RowState для Deleted строки. Напротив, если вызван RejectChanges, RowState этой строки возвратится к тому состоянию, которое до этого соответствовало состоянию удалено (Deleted).
Различие между методами Remove и Delete в том, когда имеет место фактическое удаление.
Обратите внимание: Если RowState строки установлен в New, это означает она только что была добавлен в таблицу, а если состояние отмечено как Deleted, она будет удалена из таблицы.

7.4. Работа с данными в таблице

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

[VB]
Dim CurrRows() As DataRow = workTable.Select(Nothing, Nothing, _
   System.Data.DataViewRowState.CurrentRows)
Dim list As String = System.String.Empty
Console.WriteLine("")
Console.WriteLine("Writing current rows for " & workTable.Tablename)
Dim RowNo As Integer
Dim ColNo As Integer

For RowNo = 0 to CurrRows.Count - 1
   For ColNo = 0 To workTable.Columns.Count – 1
      list = ""
      list &= workTable.Columns(colNo).ColumnName & " = " & _
         CurrRows(RowNo)(ColNo).ToString
      Console.WriteLine(list)
   Next
Next

If CurrRows.Count < 1 Then Console.WriteLine("No CurrentRows Found")

[C#]
 DataRow[] CurrRows = workTable.Select(null,
null, System.Data.DataViewRowState.CurrentRows );

 string list = System.String.Empty;

 Console.WriteLine("");
 Console.WriteLine("Writing Current rows for " + workTable.TableName );
 for (int RowNo = 0; RowNo <= CurrRows.Count -1; RowNo++)
 {
  for (int ColNo = 0; ColNo <= workTable.Columns.Count - 1; ColNo++)
  {
list = System.String.Empty;
list += workTable.Columns[ColNo].ColumnName + " = " +
CurrRows[RowNo][ColNo].ToString();
Console.WriteLine(list);
  }
 }
 if (CurrRows.Count < 1 )
 {
  Console.WriteLine("No Current Rows Found");
}

Метод Select класса DataTable дает Вам возможность отыскать набор строк, соответствующий указанным критериям. Метод возвращает массив объектов DataRow.
В примере метод отыскивает все Current строки. Что такое строка Current? DataTable поддерживает три версии каждой строки: Original, Current и Proposed.
Первоначальная (Original) строка, это такая версия строки, когда она была только что добавлено к таблице. Текущая (Current) версия строки содержит любые изменения, которые были сделаны к строке. Если Вы выполняете изменение в строке к необходимому значению, новое значение будет текущим значением, в то время как значение до изменений можно считать первоначальным значением. Промежуточная (Proposed) версия возможна при особых обстоятельствах и в течение короткого периода времени. Proposed версия представляет состояние, промежуточное между Original и Current. Строка переходит в промежуточное состояние, когда система или пользователь вызывают BeginEdit и редактируют строку.

[VB]
workRow.BeginEdit
' Make some changes to the row.
If ValidateColValue(proposedValue)
   workRow.EndEdit
Else
   workRow.CancelEdit
End If

[C#]
workRow.BeginEdit
// make some changes to the row
if (ValidateColValue(proposedValue))
   workRow.EndEdit;
else
   workRow.CancelEdit;

Это означает, что в течение состояния строки Proposed, Вы можете применять логику проверки правильности значений отдельных столбцов, анализируя результаты события ColumnChange объекта DataTable. Таблица передаёт ссылку на изменяемый столбец и состояние Proposed в событии ColumnChange. Пользователь может изменять состояние Proposed или генерировать исключение, для отмены редактирования. Строка выводится из состояния Proposed, когда вызывается EndEdit. Вы можете также отменять любое изменение при редактировании, в тот момент, когда строка находится в состоянии Proposed, с помощью вызова CancelEdit.

7.5. Добавление и чтение информации об ошибках строки

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

Представленные ниже два примера показывают как выводить/читать (Add/Read) информацию об ошибке, связанной с объектом DataRow.

Пример 1:

[VB]
Public Sub AddErrorToRow(ByVal workTable As DataTable, ByVal Row As Integer, _
      ByVal ErrorString As String)
   workTable.Rows(Row).RowError = ErrorString
End Sub

[C#]
public void AddErrorToRow(DataTable workTable, int Row, string ErrorString)
{
 workTable.Rows[Row].RowError = ErrorString;
}

Пример 2:

[VB]
Public Sub WriteTableErrors(ByVal workTable As DataTable)
   If workTable.HasErrors Then
      Dim ErrorRows() As DataRow = workTable.GetErrors()
      Console.WriteLine("")
      Console.WriteLine("=================")
      Console.WriteLine("DataTable " & workTable.TableName & " has " & _
         ErrorRows.Count.ToString & " Errors!")
      Dim i As Integer
      For i = 0 to Errors.Count – 1
         Console.WriteLine("Row Error for row " & _
         ErrorRows(i)("CustID").ToString &  "Error Msg =" & _
         ErrorRows(i).RowError)
       Next
   Else
   Console.WriteLine("")
   Console.WriteLine("=================")
      Console.WriteLine("DataTable " + workTable.TableName + " Has no errors")
   End If
End Sub

[C#]
public void WriteTableErrors(DataTable workTable)
{
 if ( workTable.HasErrors )
 {
  DataRow[] ErrorsRows = workTable.GetErrors();
  Console.WriteLine("");
  Console.WriteLine("=================");
  Console.WriteLine("DataTable " + workTable.TableName + " has " +
ErrorsRows.Count.ToString() + " Errors!");
 
  for (int i = 0; i <= ErrorsRows.Count -1; i++)
  {
Console.WriteLine("Row Error for row " +
ErrorsRows[i]["CustID"].ToString() + " Error Msg=" +
ErrorsRows[i].RowError);
  }
 }
 else
 {
Console.WriteLine("");
Console.WriteLine("=================");
Console.WriteLine("DataTable " + workTable.TableName + " Has no errors");
 }
}

Первый пример берет номер строки (Row) и добавляет сообщение об ошибках (ErrorString). Второй пример печатает RowError для каждой строки workTable, используя ассоциируемый с ошибками текст.

7.6. Принятие или отклонение изменений строк

Если Вы уверены относительно внесённых изменений, необходимо принять эти изменения и передать текущие значения строки, чтобы быть установить состояние Current. (Обратите внимание, что всё это относится к данным, находящимся DataSet, который размещён в памяти). Принятие или отклонение изменений в DataSet вызовет обновление данных в источнике (для получения дополнительной информации, прочтите ADO.NET Managed Providers).
Представленный ниже код показывает два пути принятия изменений строки.

Пример 1:

[VB]
workTable.AcceptChanges()

[C#]
workTable.AcceptChanges();

Пример 2:

[VB]
Public Function AcceptChanges(ByVal workTable As DataTable) As Integer
   Dim acceptedRows As Integer = 0
   Dim i As Integer
   For i = 0 To workTable.Rows.Count – 1
      If Not workTable.Rows(i).HasErrors Then
         acceptedRows += 1
         workTable.Rows(i).AcceptChanges()
      Else
         workTable.Rows(i).RejectChanges()
      End If
   End If
   AcceptChanges = acceptedRows
End Function

[C#]
public int AcceptChanges(DataTable workTable)
{
 int acceptedRows = 0;

 for (int i = 0; i <= workTable.Rows.Count -1 ; i++)
 {
 if (!workTable.Rows[i].HasErrors )
 {
       acceptedRows++;
       workTable.Rows[i].AcceptChanges();
 }
 else
  {
    workTable.Rows[i].RejectChanges();
  }
 }
 return acceptedRows;
}

Первый пример принимает все изменения, которые были сделаны к таблице. Второй пример вносит изменения в цикле и принимает каждое с помощью приёма Row-by-Row. Для исполнения AcceptChanges, код примера исследует свойство HasErrors.

[VB]
If Not workTable.Rows(i).HasErrors) Then

[C#]
if (!workTable.Rows[i].HasErrors )

DataRow позволяет Вам вводить строку ошибки и ассоциировать её со строкой в таблице. Если любой строке в таблице ассоциировали строку ошибки для этой строки, свойство HasErrors возвратит true. Во втором примере выполняется исследование строк, и изменения принимаются для тех, которые не имеют ошибок.
Как только изменения в строке приняты, первоначальные состояние строки заменяется на текущее состояние. Строка, которая рассматривалась, будет окончательно изменена.

7.7. Работа с событиями DataTable

Объект DataTable обеспечивает ряд событий, которые могут быть обработаны кодом пользователя. Эти события включают:

- ColumnChange
- RowChanged
- RowChanging
- RowDeleting
- RowDeleted

В представленном ниже примере, создаются три метода: MyColChanging, MyRowChanging и MyRowChanged. Каждый из этих методов вызывается, когда столбец или строка будет изменена.

[VB]
workTable.AddOnColumnChanging _
(New System.Data.DataColumnChangeEventHandler(AddressOf me.MyColChanging))
workTable.AddOnRowChanging _
(New System.Data.DataRowChangeEventHandler(AddressOf me.MyRowChanging))
workTable.AddOnRowChanged _
(New System.Data.DataRowChangeEventHandler(AddressOf me.MyRowChanged))

Private Sub MyColChanging _
(ByVal sender As Object, ByVal e As DataColumnChangeEventArgs)

End Sub

Private Sub MyRowChanging _
(ByVal sender As Object, ByVal e As DataRowChangeEventArgs)

'Add this to capture the row changing event
 Console.WriteLine("Adding row " + e.Row[0].ToString())

End Sub

Private Sub MyRowChanged _
(ByVal sender As Object, ByVal e As DataRowChangeEventArgs)
   If e.Action = Delete Then
      Console.WriteLine("deleting row " & e.Row(0).ToString)
   End If
End Sub

[C#]
workTable.AddOnColumnChanging(new DataColumnChangeEventHandler(MyColChanging));
workTable.AddOnRowChanging(new DataRowChangeEventHandler(MyRowChanging));
workTable.AddOnRowChanged(new DataRowChangeEventHandler(MyRowChanged));


Public void MyColChanging(object sender, DataColumnChangeEventArgs e)
{
}

public void MyRowChanging(object sender, DataRowChangeEventArgs e) {
 //Add this to capture the row changing event
 Console.WriteLine("Adding row " + e.Row[0].ToString());
}

public void MyRowChanged(object sender, DataRowChangeEventArgs e){
  if (e.Action == Delete)
  {
   Console.WriteLine("deleting row " + e.Row[0].ToString());
  }
}

7.8. Фильтрация и сортировка строк в таблице

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

[VB]
Dim CurrRows() As DataRow = workTable.Select(Nothing, Nothing, _
   System.Data.DataViewRowState.CurrentRows)

[C#]
DataRow[] CurrRows = workTable.Select(null,
null, System.Data.DataViewRowState.CurrentRows );

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

[VB]
Dim myNames() As DataRow = workTable.Select("CustomerLastName = 'Smith'"), _
"CustomerNameFirst", System.Data.DataViewRowState.CurrentRows)

[C#]
DataRow[] myNames = worktable.Select("CustomerNameLast = 'Smith'", "CustomerNameFirst", System

7.9. Optimistic Concurrency

В многопользовательской среде, есть два основных способа обновления данных в СУБД: Optimistic и Pessimistic Concurrency (параллелизм).
Pessimistic Concurrency - система блокировок, которая не позволяет пользователю изменять данные, которые используют другие пользователи. В Pessimistic модели, когда пользователь исполняет действие, которое устанавливает блокировку, другие пользователи не могут исполнять действия, которые вошли бы в противоречие с блокировкой, пока владелец блокировки не снимет её. Эта модель главным образом используется в средах с высокой конкуренцией использования данных, где стоимость защиты данных с помощью блокировок меньше, чем стоимость обслуживания отката транзакций, если происходят конфликты параллелизма.
Таким образом в Pessimistic модели, когда пользователь читает отчет с намерением его изменения, он устанавливает блокировку. Пока этот пользователь не закончил модификацию и не освободит блокировку, никто не сможет изменять этот отчет.
Напротив, в Optimistic Concurrency модели пользователи не блокируют данные при чтении. Когда пользователь хочет модифицировать отчет, приложение должно проверить, изменил ли другой пользователь отчет. Optimistic Concurrency вообще используется в средах с низкой конкуренцией за данные.
В Optimistic Concurrency есть традиционно три пути идентификации изменений сделанных в отчёте. Первый способ просто сравнивает все значения столбца, которые считаны из базы данных и текущие значения.

Например:

13:00. User1 читает строку в базе данных со следующими значениями:

CustID             LastName                     FirstName
101                  Smith                            Bob

Column Name Original Value Current Value Value in DB
CustID 101 101 101
LastName Smith Smith Smith
FirstName Bob Bob Bob

13:01. User2 читает ту же самую строку
13:03. User2 изменяет имя Bob на Robert и обновляет базу данных.

Column Name Original Value Current Value Value in DB
CustID 101 101 101
LastName Smith Smith Smith
FirstName Bob Robert Bob

Так как во время модификации значения в базе данных соответствуют оригиналу, модификация для user2 проходит успешно.

13:05. User1 изменяет имя Bob на James и пробует обновить строку.

Column Name Original Value Current Value Value in DB
CustID 101 101 101
LastName Smith Smith Smith
FirstName Bob James Robert

Так как первоначальное значение для user1 было Bob, а значение в базе данных из-за модификации user2 стало Robert, попытка модификации имени для user1 окончится сбоем.
Второй и третий путь Optimistic Concurrency очень похожи друг на друга: используется или date-time stamp столбец или столбец версии строки. Всякий раз, когда пользователь успешно модифицирует строку, date-time stamp (или версия строки) столбец устанавливается на новое, уникальное значение.
Таким образом, везде, где пользователь пытается модифицировать строку, приложение сравнивает оригинальное значение date-time stamp (или версия строки) и текущую версию в базе данных. Если значения идентичны, значит строка не изменилась и модификация может быть выполнена (с присвоением нового значения для date-time stamp или столбца версии строки).
Исполнение такого SQL запроса достаточно простое:

Update Table1 set x = newx1, y = newy where datetimestamp = originaldatetime

ИЛИ

Update Table1 set x = newx1, y = newy where rowversion = originalrowversion

Чтобы сохранять date-time stamp или точную версию строки, используйте триггер для таблицы, чтобы значения модифицировались при любых изменениях в строке.
Тем не менее, другой способ отслеживания модификаций состоит в применении хранимых процедур. В VS7 и DataSetCommand Configuration Wizard (DCW) включена автоматическая поддержка этих задач, что весьма удобно, если Вы используете их для создания хранимых процедур. Представленный ниже пример отслеживает модификацию средствами DCW, через хранимую процедуру, сгенерированную для таблицы авторов базы данных pubs. Обратите внимание, что список параметров этой хранимой процедура фактически содержит два набора параметров. Второй набор (@param1 - @param9), это просто копия первых девяти параметров.

CREATE PROCEDURE AuthorUpdate
(
   @au_id id,
   @au_lname varchar(40),
   @au_fname varchar(20),
   @phone char(12),
   @address varchar(40),
   @city varchar(20),
   @state char(2),
   @zip char(5),
   @contract bit,
   @Param1 id,
   @Param2 varchar(40) /* Optimistic Concurrency Check */,
   @Param3 varchar(20) /* Optimistic Concurrency Check */,
   @Param4 varchar(40) /* Optimistic Concurrency Check */,
   @Param5 varchar(20) /* Optimistic Concurrency Check */,
   @Param6 bit /* Optimistic Concurrency Check */,
   @Param7 char(12) /* Optimistic Concurrency Check */,
   @Param8 char(2) /* Optimistic Concurrency Check */,
   @Param9 char(5) /* Optimistic Concurrency Check */
)
AS
   SET NOCOUNT OFF;
   UPDATE authors SET au_id = @au_id, au_lname = @au_lname, au_fname = @au_fname, phone = @phone, address = @address, city = @city, state = @state, zip = @zip, contract = @contract
   WHERE (au_id = @Param1) AND (address = @Param2) AND (au_fname = @Param3) AND (au_lname = @Param4) AND (city = @Param5) AND (contract = @Param6) AND (phone = @Param7) AND (state = @Param8) AND (zip = @Param9);

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

Au_id = "999-99-9999"
Au_Lname = "Smith"
Au_Fname = "Jane"
Phone = "111 111-1111"
Address = "123 Main"
City = "Redmond"
State = "WA"
Zip = "98052"
Contract = 1

Предположим, что в результате выполненной Вами модификации изменится телефонный номер на 222 111-1111. Однако, ели до вашей модификации, но после того, как Вы считали данные, другой пользователь изменит фамилию Jane со Smith на Doe, при попытке выполнения Вами модификации, она не сможет быть выполнена, потому что столбец Au_Lname больше не соответствует первоначальному значению.
Таким образом, в коде, обслуживающем Вашу форму или объект, Вы можете устроить проверку строк, на которые воздействуют другие пользователи, чтобы определить, исполнена ли ваша транзакция или нет.

7.10. Исполнение транзакций

Транзакции используются, чтобы управлять блокировками в базах данных. Например, в стандартных бухгалтерских проводках, необходимо дебетовать один счёт и в то же самое время кредитовать другой. Из-за возможных ошибок компьютера, вызванных сбоем в его работе (отключение электропитания, потеря сети и т.д.), есть вероятность того, что одна запись изменена/добавлена, а другая нет. Транзакции дают возможность избежать таких ситуаций. Так же, как в ADO, транзакции в ADO.NET обрабатываются на уровне базы данных: т.е. ваша база данных должна поддержать транзакции.
Есть три основных команды для транзакций: begin, commit и rollback. Begin отмечает начало транзакции. Все процедуры, используемые между Begin и следующей командой (или Rollback или Commit) рассматриваются частью транзакции. Рассмотрим следующий пример команды T-SQL:

begin transaction

insert into account (account,amount,debitcredit) values (100,100,'d')
insert into account (account,amount,debitcredit) values (300,100,'c')

if (@@ERROR > 0)
rollback

else
commit

В ADO.NET (как и в ADO) Вы можете управлять транзакциями через объект Connection. Фактически, когда Вы используете ADOConnection, используется та же самая основная модель транзакций OLEDB. Поэтому, если Вы могли определять транзакции для вашей базы данных используя ADO, Вы также можете определять их и средствами ADO.NET.
Важно! Объект DataSet также имеет commit модель (AcceptChanges, RejectChanges), но это не затрагивает базу данных. Эта commit модель применяется только для кэша данных в одном DataSet.

Доступ к данным |Доступ к данным с помощью ADO.NET | 7. Работа с данными в таблицах ADO.NET Дальше »
Скачать электронную карту Ангарска бесплатно
Сайт управляется системой uCoz