Сохранение данных обратно в базу данных в приложениях платформа .NET Framework

Примечание.

Наборы данных и связанные классы являются устаревшими технологиями платформа .NET Framework с начала 2000-х годов, которые позволяют приложениям работать с данными в памяти во время отключения приложений от базы данных. Они особенно полезны для приложений, которые позволяют пользователям изменять данные и сохранять изменения обратно в базу данных. Хотя наборы данных оказались очень успешными, мы рекомендуем новым приложениям .NET использовать Entity Framework Core. Entity Framework предоставляет более естественный способ работы с табличными данными в виде объектных моделей, и он имеет более простой интерфейс программирования.

Набор данных — это копия данных в памяти. При изменении этих данных рекомендуется сохранить эти изменения обратно в базу данных. Это можно сделать одним из трех способов:

  • Вызов одного из Update методов TableAdapter

  • Вызов одного из DBDirect методов TableAdapter

  • UpdateAll Вызов метода в TableAdapterManager, который Visual Studio создает для вас, когда набор данных содержит таблицы, связанные с другими таблицами в наборе данных.

При привязке таблиц наборов данных к элементам управления на странице Windows Form или XAML архитектура привязки данных выполняет всю работу.

Если вы знакомы с TableAdapters, вы можете перейти непосредственно к одному из следующих разделов:

Раздел Описание
Вставка новых записей в базу данных Как выполнять обновления и вставки с помощью объектов TableAdapters или Command
Обновление данных с помощью адаптера таблицы Как выполнять обновления с помощью TableAdapters
Иерархическое обновление Как выполнять обновления из набора данных с двумя или более связанными таблицами
Обработка исключения параллельности Обработка исключений при попытке двух пользователей одновременно изменить одни и те же данные в базе данных
Практическое руководство. Сохранение данных с помощью транзакции Как сохранить данные в транзакции с помощью системы. Пространство имен транзакций и объект TransactionScope
Сохранение данных в транзакции Пошаговое руководство по созданию приложения Windows Forms для демонстрации сохранения данных в базе данных внутри транзакции
Сохранение данных в базе данных (несколько таблиц) Изменение записей и сохранение изменений в нескольких таблицах обратно в базу данных
Сохранение данных из объекта в базе данных Передача данных из объекта, который не входит в набор данных в базу данных с помощью метода TableAdapter DbDirect
Сохранение данных с помощью методов DBDirect адаптера таблицы Как использовать TableAdapter для отправки SQL-запросов непосредственно в базу данных
Сохранение набора данных в формате XML Сохранение набора данных в XML-документе

Двухэтапные обновления

Обновление источника данных — это двухэтапный процесс. Первым шагом является обновление набора данных с новыми записями, измененными записями или удаленными записями. Если приложение никогда не отправляет эти изменения обратно в источник данных, завершите обновление.

Если вы отправляете изменения обратно в базу данных, необходимо выполнить второй шаг. Если вы не используете элементы управления с привязкой к данным, необходимо вручную вызвать Update метод того же адаптера tableAdapter (или адаптера данных), который использовался для заполнения набора данных. Однако можно также использовать различные адаптеры, например для перемещения данных из одного источника данных в другой или для обновления нескольких источников данных. Если вы не используете привязку данных и сохраняете изменения для связанных таблиц, необходимо вручную создать переменную автогенерированного TableAdapterManager класса, а затем вызвать его UpdateAll метод.

Концептуальная схема обновлений набора данных

Набор данных содержит коллекции таблиц, которые содержат коллекции строк. Если вы планируете обновить базовый источник данных позже, при добавлении или удалении строк необходимо использовать методы в DataTable.DataRowCollection свойстве. Эти методы выполняют отслеживание изменений, необходимое для обновления источника данных. При вызове RemoveAt коллекции в свойстве Rows удаление не будет передано обратно в базу данных.

Объединение наборов данных

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

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

DataRowVersion Целевой набор данных Исходный набор данных
Исходная Джеймс Уилсон Джеймс С. Уилсон
Текущие Джим Уилсон Джеймс С. Уилсон

Вызов метода в предыдущей Merge таблице с preserveChanges=false targetDataset.Merge(sourceDataset) результатами следующих данных:

DataRowVersion Целевой набор данных Исходный набор данных
Исходная Джеймс С. Уилсон Джеймс С. Уилсон
Текущие Джеймс С. Уилсон Джеймс С. Уилсон

Вызов метода с preserveChanges = true targetDataset.Merge(sourceDataset, true) результатами Merge в следующих данных:

DataRowVersion Целевой набор данных Исходный набор данных
Исходная Джеймс С. Уилсон Джеймс С. Уилсон
Текущие Джим Уилсон Джеймс С. Уилсон

Внимание

preserveChanges = true В сценарии, если RejectChanges метод вызывается в записи в целевом наборе данных, он отменить изменения исходные данные из исходного набора данных. Это означает, что при попытке обновить исходный источник данных с целевым набором данных может не быть в состоянии найти исходную строку для обновления. Нарушение параллелизма можно предотвратить, заполнив другой набор данных обновленными записями из источника данных, а затем выполнив слияние, чтобы предотвратить нарушение параллелизма. (Нарушение параллелизма возникает, когда другой пользователь изменяет запись в источнике данных после заполнения набора данных.)

Ограничения обновления

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

Чтобы предотвратить нарушения преждевременных ограничений, можно временно приостановить ограничения обновления. Это служит двумя целями:

  • Это предотвращает возникновение ошибки после завершения обновления одного столбца, но не начало обновления другого.

  • Он предотвращает возникновение определенных событий обновления (события, которые часто используются для проверки).

Примечание.

В Windows Forms архитектура привязки данных, встроенная в ограничение datagrid, приостанавливает проверка, пока фокус не перейдет из строки, и вам не нужно явно вызывать BeginEditEndEditметоды или CancelEdit методы.

Ограничения автоматически отключаются при Merge вызове метода в наборе данных. Когда слияние завершится, при наличии ограничений для набора данных, который не может быть включен, ConstraintException создается исключение. В этой ситуации EnforceConstraints для свойства задано false, значение, и перед сбросом свойства trueнеобходимо устранить все нарушения ограниченийEnforceConstraints.

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

Дополнительные сведения о приостановке событий см. в разделе "Отключение ограничений при заполнении набора данных".

Ошибки обновления набора данных

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

Обслуживание сведений об изменениях

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

Свойство RowState

RowState Свойство DataRow объекта — это значение, которое предоставляет сведения о состоянии определенной строки данных.

В следующей таблице приведены возможные значения перечисления DataRowState :

Значение DataRowState Description
Added Строка добавлена в качестве элемента DataRowCollection. (Строка в этом состоянии не имеет соответствующей исходной версии, так как она не существовала при вызове последнего AcceptChanges метода).
Deleted Строка была удалена с помощью DeleteDataRow объекта.
Detached Строка создана, но не является частью любой DataRowCollection. DataRow Объект находится в этом состоянии сразу после его создания, прежде чем он был добавлен в коллекцию, и после удаления из коллекции.
Modified Значение столбца в строке изменилось каким-то образом.
Unchanged Строка не изменилась с момента AcceptChanges последнего вызова.

DataRowVersion - перечисление

Наборы данных поддерживают несколько версий записей. Поля DataRowVersion используются при получении значения, найденного в DataRowItem[] свойстве или GetChildRows методе DataRow объекта.

В следующей таблице приведены возможные значения перечисления DataRowVersion :

Значение DataRowVersion Description
Current Текущая версия записи содержит все изменения, выполненные в записи с момента последнего AcceptChanges вызова. Если строка удалена, текущая версия отсутствует.
Default Значение записи по умолчанию, определенное схемой набора данных или источником данных.
Original Исходная версия записи — это копия записи, так как это было последнее время, когда изменения были зафиксированы в наборе данных. Практически это, как правило, версия записи, считываемая из источника данных.
Proposed Предлагаемая версия записи, которая доступна временно во время обновления, то есть между тем, когда вы вызвали BeginEdit метод и EndEdit метод. Как правило, вы обращаетесь к предлагаемой версии записи в обработчике для события, RowChangingнапример. Вызов CancelEdit метода изменяет изменения и удаляет предлагаемую версию строки данных.

Исходные и текущие версии полезны при передаче сведений об обновлении в источник данных. Как правило, при отправке обновления в источник данных новая информация для базы данных находится в текущей версии записи. Сведения из исходной версии используются для поиска записи для обновления.

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

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

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

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

Получение измененных записей

Это распространенная практика не обновлять каждую запись в наборе данных. Например, пользователь может работать с элементом управления Windows Forms DataGridView , отображающим множество записей. Однако пользователь может обновить только несколько записей, удалить его и вставить новый. Наборы данных и таблицы данных предоставляют метод (GetChanges) для возврата только измененных строк.

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

GetChanges Сама по себе возвращает все измененные записи. В отличие от этого, передав требуемый DataRowState параметр GetChanges в метод, можно указать подмножество измененных записей: только что добавленные записи, записи, помеченные для удаления, отсоединенных записей или измененных записей.

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

Фиксация изменений в наборе данных

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

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

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

  • После отправки изменений из набора данных в источник данных (но не раньше, так как вы потеряете сведения об изменениях, необходимых для отправки изменений в базу данных).

Вы можете зафиксировать ожидающие изменения в наборе данных, вызвав AcceptChanges метод. Как правило, AcceptChanges вызывается в следующее время:

  • После загрузки набора данных. При загрузке набора данных путем вызова метода TableAdapter Fill адаптер автоматически фиксирует изменения. Однако при загрузке набора данных путем объединения в него другого набора данных необходимо зафиксировать изменения вручную.

    Примечание.

    Вы можете запретить адаптеру автоматически зафиксировать изменения при вызове Fill метода, задав AcceptChangesDuringFill для свойства адаптера значение false. Если задано falseзначение , RowState то для каждой строки, вставленной во время заполнения, задано значение Added.

  • После отправки изменений набора данных в другой процесс, например веб-службы XML.

    Внимание

    Фиксация изменения таким образом удаляет любые сведения об изменениях. Не фиксируйте изменения до тех пор, пока не завершите выполнение операций, требующих от приложения знать, какие изменения были внесены в набор данных.

Этот метод выполняет следующее:

  • Записывает версию записи в ее Original версию и перезаписывает Current исходную версию.

  • Удаляет любую строку, RowState в которой задано Deletedсвойство.

  • RowState Задает для свойства записи Unchangedзначение .

Метод AcceptChanges доступен на трех уровнях. Его можно вызвать в объекте DataRow , чтобы зафиксировать изменения только для этой строки. Вы также можете вызвать его в объекте DataTable для фиксации всех строк в таблице. Наконец, можно вызвать его для DataSet объекта, чтобы зафиксировать все ожидающие изменения во всех записях всех таблиц набора данных.

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

Способ Результат
System.Data.DataRow.AcceptChanges Изменения фиксируются только в определенной строке.
System.Data.DataTable.AcceptChanges Изменения фиксируются во всех строках в определенной таблице.
System.Data.DataSet.AcceptChanges Изменения фиксируются во всех строках во всех таблицах набора данных.

Примечание.

Если вы загружаете набор данных путем вызова метода TableAdapter Fill , вам не нужно явно принимать изменения. По умолчанию Fill метод вызывает AcceptChanges метод после завершения заполнения таблицы данных.

Связанный метод RejectChangesотменяет эффект изменений путем копирования Original версии обратно в Current версию записей. Он также задает RowState для каждой записи значение Unchanged.

Проверка данных

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

Данные можно проверить несколькими способами:

  • В бизнес-уровне добавьте код в приложение для проверки данных. Набор данных — это одно место, где это можно сделать. Набор данных предоставляет некоторые преимущества внутренней проверки, например возможность проверки изменений в виде значений столбцов и строк. Дополнительные сведения см. в разделе "Проверка данных в наборах данных".

  • На уровне презентации добавьте проверку в формы. Дополнительные сведения см. в разделе "Проверка ввода пользователей" в Windows Forms.

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

    Внимание

    При использовании команд данных со свойствомCommandType, заданным Textдля свойства, тщательно проверка сведения, отправляемые клиентом перед передачей в базу данных. Злоумышленники могут попытаться отправить (внедрить) модифицированные или добавочные инструкции SQL и получить незаконный доступ к базе данных или повредить ее. Прежде чем передавать входные данные пользователя в базу данных, всегда убедитесь, что информация действительна. Рекомендуется всегда использовать параметризованные запросы или хранимые процедуры, когда это возможно.

Передача обновлений в источник данных

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

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

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Unchanged)    c400         Nancy Buchanan    Pending

Приложение изменяет состояние Нэнси Бьюкенана на "Предпочтительный". В результате этого изменения значение RowState свойства для этой строки изменяется с UnchangedModified. Значение RowState свойства для первой строки остается Unchanged. Теперь таблица данных выглядит следующим образом:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Modified)     c400         Nancy Buchanan    Preferred

Теперь приложение вызывает Update метод для передачи набора данных в базу данных. Метод проверяет каждую строку в свою очередь. Для первой строки метод не передает инструкцию SQL в базу данных, так как эта строка не изменилась, так как изначально она была получена из базы данных.

Однако во второй строке Update метод автоматически вызывает правильную команду данных и передает ее в базу данных. Конкретный синтаксис инструкции SQL зависит от диалекта SQL, поддерживаемого базовым хранилищем данных. Но следует отметить следующие общие признаки передаваемой инструкции SQL:

  • Передаваемая инструкция SQL — это UPDATE инструкция. Адаптер знает, что использует UPDATE оператор, так как значение RowState свойства равно Modified.

  • Передаваемая инструкция SQL содержит WHERE предложение, указывающее, что целевой объект инструкции UPDATE — строка, в которой находится CustomerID = 'c400'. Эта часть инструкции SELECT отличает целевую строку от всех остальных, так как CustomerID это первичный ключ целевой таблицы. Сведения о предложении являются производными от исходной версии записи (DataRowVersion.Original), если значения, необходимые для WHERE идентификации строки, изменились.

  • Передаваемая инструкция SQL включает SET предложение, чтобы задать новые значения измененных столбцов.

    Примечание.

    Если для свойства TableAdapter UpdateCommand задано имя хранимой процедуры, адаптер не создает инструкцию SQL. Вместо этого он вызывает хранимую процедуру с соответствующими параметрами, переданными в.

Передача параметров

Обычно используются параметры для передачи значений записей, которые будут обновляться в базе данных. Когда метод TableAdapter Update запускает инструкцию UPDATE , необходимо заполнить значения параметров. Он получает эти значения из Parameters коллекции для соответствующей команды данных — в данном случае UpdateCommand объект в TableAdapter.

Если вы использовали средства Visual Studio для создания адаптера данных, UpdateCommand объект содержит коллекцию параметров, соответствующих каждому заполнителю параметров в инструкции.

Свойство System.Data.SqlClient.SqlParameter.SourceColumn каждого параметра указывает на столбец в таблице данных. Например, SourceColumn свойство для au_idOriginal_au_id параметров имеет значение любого столбца в таблице данных, содержащего идентификатор автора. При запуске метода адаптера Update он считывает столбец идентификатора автора из обновляемой записи и заполняет значения в инструкцию.

UPDATE В инструкции необходимо указать как новые значения (те, которые будут записаны в запись), так и старые значения (чтобы запись была расположена в базе данных). Таким образом, существует два параметра для каждого значения: один для SET предложения и другой для WHERE предложения. Оба параметра считывают данные из обновляемой записи, но они получают разные версии значения столбца на основе свойства параметра SourceVersion . Параметр для предложения получает текущую версию, а параметр для SETWHERE предложения получает исходную версию.

Примечание.

Вы также можете задать значения в коллекции самостоятельно в Parameters коде, которые обычно выполняются в обработчике событий для события адаптера RowChanging данных.