DataAdapter によるデータ ソースの更新Updating Data Sources with DataAdapters

UpdateDataAdapter メソッドを呼び出して、変更を DataSet からデータ ソースに反映します。The Update method of the DataAdapter is called to resolve changes from a DataSet back to the data source. Update メソッドは、Fill メソッドと同様に、引数として DataSet のインスタンスおよびオプションの DataTable オブジェクトまたは DataTable 名を受け取ります。The Update method, like the Fill method, takes as arguments an instance of a DataSet, and an optional DataTable object or DataTable name. DataSet のインスタンスは、行われた変更点を格納する DataSet です。DataTable は、変更点の取得元のテーブルです。The DataSet instance is the DataSet that contains the changes that have been made, and the DataTable identifies the table from which to retrieve the changes. DataTable を指定しなかった場合、DataTable 内の最初の DataSet が使用されます。If no DataTable is specified, the first DataTable in the DataSet is used.

Update メソッドを呼び出すと、DataAdapter は、既に加えられた変更を解析し、適切なコマンド (INSERT、UPDATE、または DELETE) を実行します。When you call the Update method, the DataAdapter analyzes the changes that have been made and executes the appropriate command (INSERT, UPDATE, or DELETE). DataAdapterDataRow へ加えられた変更を検出すると、InsertCommandUpdateCommand、または DeleteCommand を使用してその変更を処理します。When the DataAdapter encounters a change to a DataRow, it uses the InsertCommand, UpdateCommand, or DeleteCommand to process the change. その結果、デザイン時にコマンド構文を指定し、可能な場合はストアド プロシージャを使用することにより、ADO.NET アプリケーションのパフォーマンスを最適化できます。This allows you to maximize the performance of your ADO.NET application by specifying command syntax at design time and, where possible, through the use of stored procedures. コマンドは Update を呼び出す前に明示的に設定する必要があります。You must explicitly set the commands before calling Update. Update を呼び出し、その更新に関連する適切なコマンドが存在しない場合 (たとえば、削除済みの行に関連する DeleteCommand が存在しない場合) は、例外がスローされます。If Update is called and the appropriate command does not exist for a particular update (for example, no DeleteCommand for deleted rows), an exception is thrown.

注意

SQL Server のストアド プロシージャで、DataAdapter を使用してデータを編集または削除する場合、ストアド プロシージャの定義に SET NOCOUNT ON は使用しないでください。If you are using SQL Server stored procedures to edit or delete data using a DataAdapter, make sure that you do not use SET NOCOUNT ON in the stored procedure definition. 処理された行数がゼロとして返され、DataAdapter によってコンカレンシーの競合として解釈されてしまいます。This causes the rows affected count returned to be zero, which the DataAdapter interprets as a concurrency conflict. この場合、DBConcurrencyException がスローされます。In this event, a DBConcurrencyException will be thrown.

Command パラメーターを使用して、DataSet 内の各変更行に対する SQL ステートメントまたはストアド プロシージャに入力値と出力値を指定できます。Command parameters can be used to specify input and output values for an SQL statement or stored procedure for each modified row in a DataSet. 詳細については、「 DataAdapter Parameters」を参照してください。For more information, see DataAdapter Parameters.

注意

DataTable の行を Delete することと、行を Remove することの違いを理解することが大切です。It is important to understand the difference between deleting a row in a DataTable and removing the row. Remove メソッドまたは RemoveAt メソッドを呼び出した場合、行は直ちに削除されます。When you call the Remove or RemoveAt method, the row is removed immediately. その後、DataTable または DataSetDataAdapter に渡し、Update を呼び出しても、バックエンド データ ソースの対応する行には影響しません。Any corresponding rows in the back end data source will not be affected if you then pass the DataTable or DataSet to a DataAdapter and call Update. Delete メソッドを使用した場合、行はそのまま DataTable 内に維持され、削除対象としてマークされます。When you use the Delete method, the row remains in the DataTable and is marked for deletion. その後、DataTable または DataSetDataAdapter に渡し、Update を呼び出すと、バックエンド データ ソースから対応する行が削除されます。If you then pass the DataTable or DataSet to a DataAdapter and call Update, the corresponding row in the back end data source is deleted.

DataTable を単一データベース テーブルに割り当てたり、単一データベースから生成する場合は、DbCommandBuilder オブジェクトを利用して自動的に DeleteCommandInsertCommand オブジェクト、UpdateCommand オブジェクト、および DataAdapter オブジェクトを生成できます。If your DataTable maps to or is generated from a single database table, you can take advantage of the DbCommandBuilder object to automatically generate the DeleteCommand, InsertCommand, and UpdateCommand objects for the DataAdapter. 詳細については、「 CommandBuilders を使用したコマンドの生成」を参照してください。For more information, see Generating Commands with CommandBuilders.

UpdatedRowSource を使用した値の DataSet への割り当てUsing UpdatedRowSource to Map Values to a DataSet

DataTable オブジェクトの DataAdapter プロパティを使用すると、UpdatedRowSource の Update メソッドを呼び出した後、データ ソースから返された値を DbCommand に割り当てる方法を制御できます。You can control how the values returned from the data source are mapped back to the DataTable following a call to the Update method of a DataAdapter, by using the UpdatedRowSource property of a DbCommand object. UpdatedRowSource プロパティを UpdateRowSource 列挙型の値の 1 つに設定することで、DataAdapter コマンドが返した出力パラメーターを無視するか、DataSet 内の変更行に適用するかを制御できます。By setting the UpdatedRowSource property to one of the UpdateRowSource enumeration values, you can control whether output parameters returned by the DataAdapter commands are ignored or applied to the changed row in the DataSet. 最初に返された行 (存在する場合) を、DataTable 内の変更行に適用するかどうかを指定することもできます。You can also specify whether the first returned row (if it exists) is applied to the changed row in the DataTable.

UpdateRowSource 列挙型のさまざまの値と、それらの値が DataAdapter で使用されるコマンドの動作にどのように影響するかを次の表で説明します。The following table describes the different values of the UpdateRowSource enumeration and how they affect the behavior of a command used with a DataAdapter.

UpdatedRowSource 列挙型UpdatedRowSource Enumeration 説明Description
Both 出力パラメーターと返された結果セットの最初の行を DataSet 内の変更行に割り当てます。Both the output parameters and the first row of a returned result set may be mapped to the changed row in the DataSet.
FirstReturnedRecord 返された結果セットの最初の行のデータだけを DataSet 内の変更行に割り当てます。Only the data in the first row of a returned result set may be mapped to the changed row in the DataSet.
None 出力パラメーターまたは返された結果セットの行が無視されます。Any output parameters or rows of a returned result set are ignored.
OutputParameters 出力パラメーターだけを DataSet 内の変更行に割り当てます。Only output parameters may be mapped to the changed row in the DataSet.

Update メソッドは変更点を元のデータ ソースに反映させますが、DataSet に最後にデータを格納した後、他のクライアントがデータ ソースのデータを変更した可能性もあります。The Update method resolves your changes back to the data source; however other clients may have modified data at the data source since the last time you filled the DataSet. DataSet を現在のデータで更新するには、DataAdapter および Fill メソッドを使用します。To refresh your DataSet with current data, use the DataAdapter and Fill method. 新しい行がテーブルに追加され、更新された情報が既存の行に取り込まれます。New rows will be added to the table, and updated information will be incorporated into existing rows. Fill メソッドは、DataSet の行と SelectCommand によって返された行の主キーの値を調べて、新しい行が追加されたか、または既存の行が更新されたかを判断します。The Fill method determines whether a new row will be added or an existing row will be updated by examining the primary key values of the rows in the DataSet and the rows returned by the SelectCommand. Fill メソッドは、DataSet によって返された結果の行に一致する主キーの値を持つ SelectCommand の行を見つけた場合、SelectCommand によって返された行の情報で既存の行を更新して、既存の行の RowStateUnchanged に設定します。If the Fill method encounters a primary key value for a row in the DataSet that matches a primary key value from a row in the results returned by the SelectCommand, it updates the existing row with the information from the row returned by the SelectCommand and sets the RowState of the existing row to Unchanged. SelectCommand によって返された行の主キーの値が、DataSet のどの行の主キーの値にも一致しない場合、Fill メソッドは、RowStateUnchanged の新しい行を追加します。If a row returned by the SelectCommand has a primary key value that does not match any of the primary key values of the rows in the DataSet, the Fill method adds a new row with a RowState of Unchanged.

注意

SelectCommand が OUTER JOIN の結果を返す場合、DataAdapter は、生成される PrimaryKeyDataTable 値を設定しません。If the SelectCommand returns the results of an OUTER JOIN, the DataAdapter will not set a PrimaryKey value for the resulting DataTable. 自分で PrimaryKey を定義して、重複行が正しく反映されるようにする必要があります。You must define the PrimaryKey yourself to ensure that duplicate rows are resolved correctly. 詳細については、「主キーの定義」を参照してください。For more information, see Defining Primary Keys.

Updateメソッドの呼び出し時に発生する可能性のある例外を処理するためRowUpdatedに、イベントを使用して、発生した行の更新エラーに応答することができますtrue ( DataAdapter イベントの処理に関するを参照してください)。または、の前にを設定DataAdapter.ContinueUpdateOnErrorすることもできます。更新Updateの完了時に、を呼び出し、特定のRowError行のプロパティに格納されているエラー情報に応答します (行のエラー情報を参照してください)。To handle exceptions that may occur when calling the Update method, you can use the RowUpdated event to respond to row update errors as they occur (see Handling DataAdapter Events), or you can set DataAdapter.ContinueUpdateOnError to true before calling Update, and respond to the error information stored in the RowError property of a particular row when the update is complete (see Row Error Information).

注意

DataSetAcceptChanges Original DataRow 、またCurrentはでを呼び出すと、のすべての値がの値で上書きされます。DataRow DataTable DataRowCalling AcceptChanges on the DataSet, DataTable, or DataRow will cause all Original values for a DataRow to be overwritten with the Current values for the DataRow. 行を一意に識別するフィールド値が変更された場合は、AcceptChanges 呼び出しの後に Original 値がデータ ソースの値と一致しなくなります。If the field values that identify the row as unique have been modified, after calling AcceptChanges the Original values will no longer match the values in the data source. AcceptChanges は、DataAdapter の Update メソッドを呼び出す間に、各行について自動的に呼び出されます。AcceptChanges is called automatically for each row during a call to the Update method of a DataAdapter. Update メソッドの呼び出し中に元の値を維持するには、まず AcceptChangesDuringUpdateDataAdapter プロパティを false に設定するか、RowUpdated イベントのイベント ハンドラーを作成し、その StatusSkipCurrentRow に設定します。You can preserve the original values during a call to the Update method by first setting the AcceptChangesDuringUpdate property of the DataAdapter to false, or by creating an event handler for the RowUpdated event and setting the Status to SkipCurrentRow. 詳細については、「データセットの内容のマージDataAdapter イベントの処理」を参照してください。For more information, see Merging DataSet Contents and Handling DataAdapter Events.

Example

次の例ではUpdateCommand DataAdapter 、のを明示的に設定し、そのUpdateメソッドを呼び出すことで、変更された行に対する更新を実行する方法を示します。The following examples demonstrate how to perform updates to modified rows by explicitly setting the UpdateCommand of a DataAdapter and calling its Update method. UPDATE ステートメントの WHERE 句に指定したパラメーターが OriginalSourceColumn 値を使用するように設定されていることに注意してください。Notice that the parameter specified in the WHERE clause of the UPDATE statement is set to use the Original value of the SourceColumn. Current 値が既に変更されている可能性、そしてデータ ソースの値と一致していない可能性があるため、この設定は重要です。This is important, because the Current value may have been modified and may not match the value in the data source. Original 値は、データ ソースから DataTable にデータを取得するために使用された値です。The Original value is the value that was used to populate the DataTable from the data source.

private static void AdapterUpdate(string connectionString)
{
    using (SqlConnection connection =
               new SqlConnection(connectionString))
    {
        SqlDataAdapter dataAdpater = new SqlDataAdapter(
          "SELECT CategoryID, CategoryName FROM Categories",
          connection);

        dataAdpater.UpdateCommand = new SqlCommand(
           "UPDATE Categories SET CategoryName = @CategoryName " +
           "WHERE CategoryID = @CategoryID", connection);

        dataAdpater.UpdateCommand.Parameters.Add(
           "@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

        SqlParameter parameter = dataAdpater.UpdateCommand.Parameters.Add(
          "@CategoryID", SqlDbType.Int);
        parameter.SourceColumn = "CategoryID";
        parameter.SourceVersion = DataRowVersion.Original;

        DataTable categoryTable = new DataTable();
        dataAdpater.Fill(categoryTable);

        DataRow categoryRow = categoryTable.Rows[0];
        categoryRow["CategoryName"] = "New Beverages";

        dataAdpater.Update(categoryTable);

        Console.WriteLine("Rows after update.");
        foreach (DataRow row in categoryTable.Rows)
        {
            {
                Console.WriteLine("{0}: {1}", row[0], row[1]);
            }
        }
    }
}
Private Sub AdapterUpdate(ByVal connectionString As String)

    Using connection As SqlConnection = New SqlConnection( _
       connectionString)

        Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
          "SELECT CategoryID, CategoryName FROM dbo.Categories", _
          connection)

        adapter.UpdateCommand = New SqlCommand( _
          "UPDATE Categories SET CategoryName = @CategoryName " & _
           "WHERE CategoryID = @CategoryID", connection)

        adapter.UpdateCommand.Parameters.Add( _
           "@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

        Dim parameter As SqlParameter = _
           adapter.UpdateCommand.Parameters.Add( _
           "@CategoryID", SqlDbType.Int)
        parameter.SourceColumn = "CategoryID"
        parameter.SourceVersion = DataRowVersion.Original

        Dim categoryTable As New DataTable
        adapter.Fill(categoryTable)

        Dim categoryRow As DataRow = categoryTable.Rows(0)
        categoryRow("CategoryName") = "New Beverages"

        adapter.Update(categoryTable)

        Console.WriteLine("Rows after update.")
        Dim row As DataRow
        For Each row In categoryTable.Rows
            Console.WriteLine("{0}: {1}", row(0), row(1))
        Next
    End Using
End Sub

AutoIncrement 列AutoIncrement Columns

データ ソースから取得したテーブルに自動インクリメント列がある場合、自動インクリメント値をストアド プロシージャの出力パラメーターとして取得してそれをテーブルの列に割り当てるか、ストアド プロシージャまたは SQL ステートメントによって返された結果セットの最初の行の自動インクリメント値を取得するか、または DataSetRowUpdated イベントを使用して追加の SELECT コマンドを実行することによって、DataAdapter の列に値を格納できます。If the tables from your data source have auto-incrementing columns, you can fill the columns in your DataSet either by returning the auto-increment value as an output parameter of a stored procedure and mapping that to a column in a table, by returning the auto-increment value in the first row of a result set returned by a stored procedure or SQL statement, or by using the RowUpdated event of the DataAdapter to execute an additional SELECT statement. 詳細と例については、「 id またはオートナンバー値の取得」を参照してください。For more information and an example, see Retrieving Identity or Autonumber Values.

挿入、更新、削除の順序Ordering of Inserts, Updates, and Deletes

通常の条件下では、DataSet を使用して行う変更の順序をデータ ソースに送信することが重要です。In many circumstances, the order in which changes made through the DataSet are sent to the data source is important. たとえば、既存の行の主キーの値を更新し、その新しい主キーの値を外部キーとして新しい行を追加する場合、更新は挿入の前に処理する必要があります。For example, if a primary key value for an existing row is updated, and a new row has been added with the new primary key value as a foreign key, it is important to process the update before the insert.

SelectDataTable メソッドを使用すると、特定の DataRow を持つ行だけを参照する RowState 配列を返すことができます。You can use the Select method of the DataTable to return a DataRow array that only references rows with a particular RowState. その後で、返された DataRow 配列を UpdateDataAdapter メソッドに渡して変更行を処理できます。You can then pass the returned DataRow array to the Update method of the DataAdapter to process the modified rows. 更新する行のサブセットを指定することで、挿入、更新、および削除の処理順序を制御できます。By specifying a subset of rows to be updated, you can control the order in which inserts, updates, and deletes are processed.

Example

たとえば次のコードでは、テーブルの削除行を最初に処理し、次に更新行、最後に挿入行を処理します。For example, the following code ensures that the deleted rows of the table are processed first, then the updated rows, and then the inserted rows.

Dim table As DataTable = dataSet.Tables("Customers")

' First process deletes.
dataSet.Update(table.Select(Nothing, Nothing, _
  DataViewRowState.Deleted))

' Next process updates.
adapter.Update(table.Select(Nothing, Nothing, _
  DataViewRowState.ModifiedCurrent))

' Finally, process inserts.
adapter.Update(table.Select(Nothing, Nothing, _
  DataViewRowState.Added))
DataTable table = dataSet.Tables["Customers"];

// First process deletes.
adapter.Update(table.Select(null, null, DataViewRowState.Deleted));

// Next process updates.
adapter.Update(table.Select(null, null,
  DataViewRowState.ModifiedCurrent));

// Finally, process inserts.
adapter.Update(table.Select(null, null, DataViewRowState.Added));

DataAdapter を使用したデータの取得と更新Use a DataAdapter to Retrieve and Update Data

DataAdapter を使用すると、データを取得および更新できます。You can use a DataAdapter to retrieve and update the data.

  • このサンプルでは、DataAdapter.AcceptChangesDuringFill を使用してデータベース内のデータを複製します。The sample uses DataAdapter.AcceptChangesDuringFill to clone the data in the database. このプロパティが false として設定されている場合、AcceptChanges はテーブルにデータを格納するときに呼び出されず、新しく追加された行が挿入された行として処理されます。If the property is set as false, AcceptChanges is not called when filling the table, and the newly added rows are treated as inserted rows. そのため、このサンプルでは、これらの行を使用して、データベースに新しい行を挿入します。So, the sample uses these rows to insert the new rows into the database.

  • このサンプルでは、DataAdapter.TableMappings を使用して、ソース テーブルと DataTable 間のマッピングを定義します。The samples uses DataAdapter.TableMappings to define the mapping between the source table and DataTable.

  • このサンプルでは、DataAdapter.FillLoadOption を使用して、アダプターが DbDataReader から DataTable にデータを設定する方法を決定します。The sample uses DataAdapter.FillLoadOption to determine how the adapter fills the DataTable from the DbDataReader. DataTable を作成するときに、プロパティを LoadOption.Upsert または LoadOption.PreserveChanges として設定した場合のみ、データベースのデータを現在のバージョンまたは元のバージョンに書き込むことができます。When you create a DataTable, you can only write the data from database to the current version or the original version by setting the property as the LoadOption.Upsert or the LoadOption.PreserveChanges.

  • このサンプルでは、DbDataAdapter.UpdateBatchSize を使用してバッチ操作を実行することにより、テーブルを更新します。The sample will also update the table by using DbDataAdapter.UpdateBatchSize to perform batch operations.

サンプルをコンパイルして実行する前に、サンプル データベースを作成する必要があります。Before you compile and run the sample, you need to create the sample database:

USE [master]
GO

CREATE DATABASE [MySchool]

GO

USE [MySchool]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Course]([CourseID] [nvarchar](10) NOT NULL,
[Year] [smallint] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[Credits] [int] NOT NULL,
[DepartmentID] [int] NOT NULL,
 CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED
(
[CourseID] ASC,
[Year] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Department]([DepartmentID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Budget] [money] NOT NULL,
[StartDate] [datetime] NOT NULL,
[Administrator] [int] NULL,
 CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
(
[DepartmentID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]

GO

INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C1045', 2012, N'Calculus', 4, 7)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C1061', 2012, N'Physics', 4, 1)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C2021', 2012, N'Composition', 3, 2)
INSERT [dbo].[Course] ([CourseID], [Year], [Title], [Credits], [DepartmentID]) VALUES (N'C2042', 2012, N'Literature', 4, 2)

SET IDENTITY_INSERT [dbo].[Department] ON

INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (1, N'Engineering', 350000.0000, CAST(0x0000999C00000000 AS DateTime), 2)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (2, N'English', 120000.0000, CAST(0x0000999C00000000 AS DateTime), 6)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (4, N'Economics', 200000.0000, CAST(0x0000999C00000000 AS DateTime), 4)
INSERT [dbo].[Department] ([DepartmentID], [Name], [Budget], [StartDate], [Administrator]) VALUES (7, N'Mathematics', 250024.0000, CAST(0x0000999C00000000 AS DateTime), 3)
SET IDENTITY_INSERT [dbo].[Department] OFF

ALTER TABLE [dbo].[Course]  WITH CHECK ADD  CONSTRAINT [FK_Course_Department] FOREIGN KEY([DepartmentID])
REFERENCES [dbo].[Department] ([DepartmentID])
GO
ALTER TABLE [dbo].[Course] CHECK CONSTRAINT [FK_Course_Department]
GO

C#このコードサンプルを含むプロジェクト Visual Basic は、開発者コードサンプルを参照してください。C# and Visual Basic projects with this code sample can be found on Developer Code Samples.

using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using CSDataAdapterOperations.Properties;

namespace CSDataAdapterOperations.Properties {
   internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {

      private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

      public static Settings Default {
         get {
            return defaultInstance;
         }
      }

      [global::System.Configuration.ApplicationScopedSettingAttribute()]
      [global::System.Configuration.DefaultSettingValueAttribute("Data Source=(local);Initial Catalog=MySchool;Integrated Security=True")]
      public string MySchoolConnectionString {
         get {
            return ((string)(this["MySchoolConnectionString"]));
         }
      }
   }
}

class Program {
   static void Main(string[] args) {
      Settings settings = new Settings();

      // Copy the data from the database.  Get the table Department and Course from the database.
      String selectString = @"SELECT [DepartmentID],[Name],[Budget],[StartDate],[Administrator]
                                     FROM [MySchool].[dbo].[Department];

                                   SELECT [CourseID],@Year as [Year],Max([Title]) as [Title],
                                   Max([Credits]) as [Credits],Max([DepartmentID]) as [DepartmentID]
                                   FROM [MySchool].[dbo].[Course]
                                   Group by [CourseID]";

      DataSet mySchool = new DataSet();

      SqlCommand selectCommand = new SqlCommand(selectString);
      SqlParameter parameter = selectCommand.Parameters.Add("@Year", SqlDbType.SmallInt, 2);
      parameter.Value = new Random(DateTime.Now.Millisecond).Next(9999);

      // Use DataTableMapping to map the source tables and the destination tables.
      DataTableMapping[] tableMappings = {new DataTableMapping("Table", "Department"), new DataTableMapping("Table1", "Course")};
      CopyData(mySchool, settings.MySchoolConnectionString, selectCommand, tableMappings);

      Console.WriteLine("The following tables are from the database.");
      foreach (DataTable table in mySchool.Tables) {
         Console.WriteLine(table.TableName);
         ShowDataTable(table);
      }

      // Roll back the changes
      DataTable department = mySchool.Tables["Department"];
      DataTable course = mySchool.Tables["Course"];

      department.Rows[0]["Name"] = "New" + department.Rows[0][1];
      course.Rows[0]["Title"] = "New" + course.Rows[0]["Title"];
      course.Rows[0]["Credits"] = 10;

      Console.WriteLine("After we changed the tables:");
      foreach (DataTable table in mySchool.Tables) {
         Console.WriteLine(table.TableName);
         ShowDataTable(table);
      }

      department.RejectChanges();
      Console.WriteLine("After use the RejectChanges method in Department table to roll back the changes:");
      ShowDataTable(department);

      DataColumn[] primaryColumns = { course.Columns["CourseID"] };
      DataColumn[] resetColumns = { course.Columns["Title"] };
      ResetCourse(course, settings.MySchoolConnectionString, primaryColumns, resetColumns);
      Console.WriteLine("After use the ResetCourse method in Course table to roll back the changes:");
      ShowDataTable(course);

      // Batch update the table.
      String insertString = @"Insert into [MySchool].[dbo].[Course]([CourseID],[Year],[Title],
                                   [Credits],[DepartmentID])
             values (@CourseID,@Year,@Title,@Credits,@DepartmentID)";
      SqlCommand insertCommand = new SqlCommand(insertString);
      insertCommand.Parameters.Add("@CourseID", SqlDbType.NVarChar, 10, "CourseID");
      insertCommand.Parameters.Add("@Year", SqlDbType.SmallInt, 2, "Year");
      insertCommand.Parameters.Add("@Title", SqlDbType.NVarChar, 100, "Title");
      insertCommand.Parameters.Add("@Credits", SqlDbType.Int, 4, "Credits");
      insertCommand.Parameters.Add("@DepartmentID", SqlDbType.Int, 4, "DepartmentID");

      const Int32 batchSize = 10;
      BatchInsertUpdate(course, settings.MySchoolConnectionString, insertCommand, batchSize);
   }

   private static void CopyData(DataSet dataSet, String connectionString, SqlCommand selectCommand, DataTableMapping[] tableMappings) {
      using (SqlConnection connection = new SqlConnection(connectionString)) {
         selectCommand.Connection = connection;

         connection.Open();

         using (SqlDataAdapter adapter = new SqlDataAdapter(selectCommand)) {adapter.TableMappings.AddRange(tableMappings);
            // If set the AcceptChangesDuringFill as the false, AcceptChanges will not be called on a
            // DataRow after it is added to the DataTable during any of the Fill operations.
            adapter.AcceptChangesDuringFill = false;

            adapter.Fill(dataSet);
         }
      }
   }

   // Roll back only one column or several columns data of the Course table by call ResetDataTable method.
   private static void ResetCourse(DataTable table, String connectionString,
       DataColumn[] primaryColumns, DataColumn[] resetColumns) {
      table.PrimaryKey = primaryColumns;

      // Build the query string
      String primaryCols = String.Join(",", primaryColumns.Select(col => col.ColumnName));
      String resetCols = String.Join(",", resetColumns.Select(col => $"Max({col.ColumnName}) as {col.ColumnName}"));

      String selectString = $"Select {primaryCols},{resetCols} from Course Group by {primaryCols}");

      SqlCommand selectCommand = new SqlCommand(selectString);

      ResetDataTable(table, connectionString, selectCommand);
   }

   // RejectChanges will roll back all changes made to the table since it was loaded, or the last time AcceptChanges
   // was called. When you copy from the database, you can lose all the data after calling RejectChanges
   // The ResetDataTable method rolls back one or more columns of data.
   private static void ResetDataTable(DataTable table, String connectionString,
       SqlCommand selectCommand) {
      using (SqlConnection connection = new SqlConnection(connectionString)) {
         selectCommand.Connection = connection;

         connection.Open();

         using (SqlDataAdapter adapter = new SqlDataAdapter(selectCommand)) {
            // The incoming values for this row will be written to the current version of each
            // column. The original version of each column's data will not be changed.
            adapter.FillLoadOption = LoadOption.Upsert;

            adapter.Fill(table);
         }
      }
   }

   private static void BatchInsertUpdate(DataTable table, String connectionString,
       SqlCommand insertCommand, Int32 batchSize) {
      using (SqlConnection connection = new SqlConnection(connectionString)) {
         insertCommand.Connection = connection;
         // When setting UpdateBatchSize to a value other than 1, all the commands
         // associated with the SqlDataAdapter have to have their UpdatedRowSource
         // property set to None or OutputParameters. An exception is thrown otherwise.
         insertCommand.UpdatedRowSource = UpdateRowSource.None;

         connection.Open();

         using (SqlDataAdapter adapter = new SqlDataAdapter()) {
            adapter.InsertCommand = insertCommand;
            // Gets or sets the number of rows that are processed in each round-trip to the server.
            // Setting it to 1 disables batch updates, as rows are sent one at a time.
            adapter.UpdateBatchSize = batchSize;

            adapter.Update(table);

            Console.WriteLine("Successfully to update the table.");
         }
      }
   }

   private static void ShowDataTable(DataTable table) {
      foreach (DataColumn col in table.Columns) {
         Console.Write("{0,-14}", col.ColumnName);
      }
      Console.WriteLine("{0,-14}", "RowState");

      foreach (DataRow row in table.Rows) {
         foreach (DataColumn col in table.Columns) {
            if (col.DataType.Equals(typeof(DateTime)))
               Console.Write("{0,-14:d}", row[col]);
            else if (col.DataType.Equals(typeof(Decimal)))
               Console.Write("{0,-14:C}", row[col]);
            else
               Console.Write("{0,-14}", row[col]);
         }
         Console.WriteLine("{0,-14}", row.RowState);
      }
   }
}

関連項目See also