Aktualizowanie źródeł danych za pomocą elementów DataAdapter

Metoda Update metody jest wywoływana DataAdapter w celu rozwiązania zmian z DataSet powrotem do źródła danych. Metoda Update , podobnie jak Fill metoda, przyjmuje jako argumenty wystąpienie DataSetobiektu i opcjonalną DataTable nazwę lub DataTable obiekt. Wystąpienie DataSet to DataSet , które zawiera wprowadzone zmiany, oraz DataTable identyfikuje tabelę, z której mają zostać pobrane zmiany. Jeśli nie DataTable zostanie określony, zostanie użyty pierwszy DataTable w elemecie DataSet .

Podczas wywoływania Update metody DataAdapter funkcja analizuje wprowadzone zmiany i wykonuje odpowiednie polecenie (INSERT, UPDATE lub DELETE). W przypadku DataAdapter napotkania zmiany na DataRow, używa InsertCommandmetody , UpdateCommandlub DeleteCommand do przetworzenia zmiany. Dzięki temu można zmaksymalizować wydajność aplikacji ADO.NET, określając składnię poleceń w czasie projektowania i, jeśli to możliwe, za pomocą procedur składowanych. Przed wywołaniem Updatepolecenia należy jawnie ustawić polecenie . Jeśli Update jest wywoływana i odpowiednie polecenie nie istnieje dla określonej aktualizacji (na przykład nie DeleteCommand dla usuniętych wierszy), zgłaszany jest wyjątek.

Uwaga

Jeśli używasz procedur składowanych programu SQL Server do edytowania lub usuwania danych przy użyciu elementu DataAdapter, upewnij się, że nie używasz funkcji SET NOCOUNT ON w definicji procedury składowanej. Powoduje to, że liczba wierszy, których dotyczy problem, zwraca wartość zero, co DataAdapter interpretuje jako konflikt współbieżności. W tym przypadku zostanie zgłoszony element DBConcurrencyException .

Parametry polecenia mogą służyć do określania wartości wejściowych i wyjściowych instrukcji SQL lub procedury składowanej dla każdego zmodyfikowanego wiersza w obiekcie DataSet. Aby uzyskać więcej informacji, zobacz DataAdapter Parameters (Parametry elementu DataAdapter).

Uwaga

Ważne jest, aby zrozumieć różnicę między usunięciem wiersza w obiekcie DataTable a usunięciem wiersza. Po wywołaniu Remove metody or RemoveAt wiersz zostanie natychmiast usunięty. Jeśli następnie przekażesz DataTable element lub do metody DataAdapter i DataSetUpdate, wszystkie odpowiednie wiersze w źródle danych zaplecza nie będą miały wpływu. W przypadku użycia metody wiersz pozostaje w elemecie DeleteDataTable i jest oznaczony do usunięcia. Jeśli następnie przekażesz metodę DataTableDataAdapter lub DataSet do metody i Update, odpowiedni wiersz w źródle danych zaplecza zostanie usunięty.

DataTable Jeśli mapy do lub są generowane na podstawie pojedynczej tabeli bazy danych, możesz skorzystać z DbCommandBuilder obiektu , aby automatycznie wygenerować DeleteCommandobiekty , InsertCommandi UpdateCommand dla obiektu DataAdapter. Aby uzyskać więcej informacji, zobacz Generowanie poleceń za pomocą poleceń CommandBuilders.

Używanie elementu UpdatedRowSource do mapowania wartości na zestaw danych

Możesz kontrolować, jak wartości zwracane ze źródła danych są mapowane z powrotem na DataTable następujące wywołanie metody Update obiektu DataAdapterprzy użyciu UpdatedRowSource właściwości DbCommand obiektu . Ustawiając UpdatedRowSource właściwość na jedną z UpdateRowSource wartości wyliczenia, możesz kontrolować, czy parametry wyjściowe zwracane przez DataAdapter polecenia są ignorowane, czy stosowane do zmienionego wiersza w obiekcie DataSet. Można również określić, czy pierwszy zwrócony wiersz (jeśli istnieje) jest stosowany do zmienionego wiersza w pliku DataTable.

W poniższej tabeli opisano różne wartości UpdateRowSource wyliczenia i sposób ich wpływu na zachowanie polecenia używanego z elementem DataAdapter.

UpdatedRowSource— Wyliczenie opis
Both Parametry wyjściowe i pierwszy wiersz zwróconego zestawu wyników mogą być mapowane na zmieniony wiersz w pliku DataSet.
FirstReturnedRecord Tylko dane w pierwszym wierszu zwróconego zestawu wyników mogą być mapowane na zmieniony wiersz w pliku DataSet.
None Wszystkie parametry wyjściowe lub wiersze zwróconego zestawu wyników są ignorowane.
OutputParameters Tylko parametry wyjściowe mogą być mapowane na zmieniony wiersz w pliku DataSet.

Metoda Update usuwa zmiany ze źródłem danych, jednak inni klienci mogli zmodyfikować dane w źródle danych od czasu ostatniego wypełnienia elementu DataSet. Aby odświeżyć dane DataSet przy użyciu bieżących danych, użyj DataAdapter metody i Fill . Nowe wiersze zostaną dodane do tabeli, a zaktualizowane informacje zostaną włączone do istniejących wierszy. Metoda Fill określa, czy nowy wiersz zostanie dodany, czy istniejący wiersz zostanie zaktualizowany, sprawdzając wartości klucza podstawowego wierszy w DataSet tabeli i wiersze zwrócone przez SelectCommandelement . Fill Jeśli metoda napotka wartość klucza podstawowego dla wiersza wDataSet, który pasuje do wartości klucza podstawowego z wiersza w wynikach zwróconych przez SelectCommand, aktualizuje istniejący wiersz z informacjami z wiersza zwróconego przez SelectCommand i ustawia RowState istniejący wiersz na Unchangedwartość . Jeśli wiersz zwracany przez SelectCommand element ma wartość klucza podstawowego, która nie jest zgodna z żadnymi wartościami klucza podstawowego wierszy w DataSettabeli , Fill metoda dodaje nowy wiersz z wartością UnchangedRowState .

Uwaga

SelectCommand Jeśli funkcja zwraca wyniki sprzężenia ZEWNĘTRZNEgo, DataAdapter parametr nie ustawi PrimaryKey wartości wynikowej DataTable. Należy zdefiniować PrimaryKey samodzielnie, aby upewnić się, że zduplikowane wiersze są prawidłowo rozpoznawane. Aby uzyskać więcej informacji, zobacz Definiowanie kluczy podstawowych.

Aby obsłużyć wyjątki, które mogą wystąpić podczas wywoływania metody, można użyć RowUpdated zdarzenia do reagowania na błędy aktualizacji wierszy w miarę ich występowania (zobacz Obsługa zdarzeń DataAdapter) lub można ustawić DataAdapter.ContinueUpdateOnError wartość na true wartość przed wywołaniem UpdateUpdatemetody i odpowiedzieć na informacje o błędzie przechowywane we RowError właściwości określonego wiersza po zakończeniu aktualizacji (zobacz Informacje o błędzie wiersza).

Uwaga

Wywołanie AcceptChanges metody , DataTableDataSetlub DataRow spowoduje zastąpienie wszystkich Original wartości obiektu DataRow wartościami Current dla elementu DataRow. Jeśli wartości pól identyfikujące wiersz jako unikatowy zostały zmodyfikowane, po wywołaniu AcceptChangesOriginal wartości nie będą już zgodne z wartościami w źródle danych. AcceptChanges element jest wywoływany automatycznie dla każdego wiersza podczas wywołania metody Update klasy DataAdapter. Oryginalne wartości można zachować podczas wywołania metody Update, ustawiając AcceptChangesDuringUpdate najpierw właściwość DataAdapter właściwości na false lub tworząc program obsługi zdarzeń dla RowUpdated zdarzenia i ustawiając Status wartość SkipCurrentRow. Aby uzyskać więcej informacji, zobacz Scalanie zawartości zestawu danych i obsługa zdarzeń dataAdapter.

Przykład

W poniższych przykładach pokazano, jak wykonać aktualizacje zmodyfikowanych wierszy, jawnie ustawiając UpdateCommandDataAdapter metodę i wywołując metodę Update . Zwróć uwagę, że parametr określony w klauzuli WHERE instrukcji UPDATE jest ustawiony na użycie Original wartości SourceColumn. Jest to ważne, ponieważ Current wartość mogła zostać zmodyfikowana i może być niezgodna z wartością w źródle danych. Wartość Original to wartość, która została użyta do wypełnienia DataTable elementu ze źródła danych.

static void AdapterUpdate(string connectionString)
{
    using (SqlConnection connection =
               new(connectionString))
    {
        SqlDataAdapter dataAdapter = new(
          "SELECT CategoryID, CategoryName FROM Categories",
          connection)
        {
            UpdateCommand = new SqlCommand(
           "UPDATE Categories SET CategoryName = @CategoryName " +
           "WHERE CategoryID = @CategoryID", connection)
        };

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

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

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

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

        dataAdapter.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

Kolumny autoinkrementacji

Jeśli tabele ze źródła danych mają kolumny automatycznego zwiększania, możesz wypełnić kolumny w DataSet elemecie , zwracając wartość automatycznego przyrostu jako parametr wyjściowy procedury składowanej i mapując ją na kolumnę w tabeli, zwracając wartość auto-inkrementacji w pierwszym wierszu zestawu wyników zwróconego przez procedurę składowaną lub instrukcję SQL. lub za pomocą RowUpdated zdarzenia DataAdapter , aby wykonać dodatkową instrukcję SELECT. Aby uzyskać więcej informacji i przykład, zobacz Pobieranie tożsamości lub wartości autonumerowania.

Kolejność wstawiania, Aktualizacje i usuwania

W wielu okolicznościach ważna jest kolejność wysyłania zmian wprowadzonych za pośrednictwem DataSet elementu do źródła danych. Jeśli na przykład wartość klucza podstawowego dla istniejącego wiersza zostanie zaktualizowana, a nowy wiersz został dodany przy użyciu nowej wartości klucza podstawowego jako klucza obcego, ważne jest, aby przetworzyć aktualizację przed wstawieniem.

Możesz użyć Select metody , DataTable aby zwrócić tablicę DataRow , która odwołuje się tylko do wierszy z określonym RowStateelementem . Następnie można przekazać zwróconą DataRow tablicę do Update metody DataAdapter , aby przetworzyć zmodyfikowane wiersze. Określając podzestaw wierszy do zaktualizowania, można kontrolować kolejność przetwarzania wstawiania, aktualizacji i usuwania.

Na przykład poniższy kod gwarantuje, że usunięte wiersze tabeli są najpierw przetwarzane, a następnie zaktualizowane wiersze, a następnie wstawione wiersze.

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));

Używanie elementu DataAdapter do pobierania i aktualizowania danych

Aby pobrać i zaktualizować dane, możesz użyć elementu DataAdapter.

  • W przykładzie użyto elementu DataAdapter.AcceptChangesDuringFill w celu sklonowania danych w bazie danych. Jeśli właściwość jest ustawiona jako false, funkcja AcceptChanges nie jest wywoływana podczas wypełniania tabeli, a nowo dodane wiersze są traktowane jako wstawione wiersze. W tym przykładzie użyto tych wierszy do wstawienia nowych wierszy do bazy danych.

  • W przykładach użyto elementu DataAdapter.TableMappings do zdefiniowania mapowania między tabelą źródłową a tabelą DataTable.

  • W przykładzie użyto elementu DataAdapter.FillLoadOption, aby określić sposób wypełniania tabeli DataTable z elementu DbDataReader. Podczas tworzenia tabeli DataTable można zapisywać dane tylko z bazy danych do bieżącej wersji lub oryginalnej wersji, ustawiając właściwość jako LoadOption.Upsert lub LoadOption.PreserveChanges.

  • Przykład zaktualizuje również tabelę przy użyciu polecenia DbDataAdapter.UpdateBatchSize w celu wykonywania operacji wsadowych.

Przed skompilowanie i uruchomieniem przykładowej bazy danych należy utworzyć przykładową bazę danych:

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
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);
      }
   }
}

Zobacz też