Aktualizace zdrojů dat pomocí objektů DataAdapter

Volá Update se DataAdapter metoda, která řeší změny ze DataSet zdroje dat zpět. Metoda Update , podobně jako Fill metoda, přebírá jako argumenty instanci objektu DataSeta volitelný DataTable objekt nebo DataTable název. Instance DataSet je DataSet ta, která obsahuje změny, které byly provedeny, a DataTable identifikuje tabulku, ze které se mají změny načíst. Pokud není zadána žádná DataTable , použije se první DataTable v seznamu DataSet .

Při volání Update metody analyzuje změny, DataAdapter které byly provedeny, a spustí příslušný příkaz (INSERT, UPDATE nebo DELETE). DataAdapter Když narazí na DataRowzměnu , použije , použije InsertCommand, UpdateCommandnebo DeleteCommand ke zpracování změny. To vám umožní maximalizovat výkon ADO.NET aplikace zadáním syntaxe příkazů v době návrhu a tam, kde je to možné, prostřednictvím uložených procedur. Před voláním Updateje nutné explicitně nastavit příkazy . Pokud Update je volána a příslušný příkaz neexistuje pro konkrétní aktualizaci (například ne DeleteCommand pro odstraněné řádky), vyvolá se výjimka.

Poznámka:

Pokud k úpravě nebo odstranění dat používáte uložené procedury SQL Serveru, DataAdapterujistěte se, že v definici uložené procedury nepoužíváte funkci SET NOCOUNT ON. To způsobí, že počet ovlivněných řádků bude nulový, což DataAdapter interpretuje jako konflikt souběžnosti. V tomto případě dojde k DBConcurrencyException vyvolání.

Parametry příkazu lze použít k zadání vstupních a výstupních hodnot pro příkaz SQL nebo uloženou proceduru pro každý upravený řádek v objektu DataSet. Další informace naleznete v tématu DataAdapter Parameters.

Poznámka:

Je důležité pochopit rozdíl mezi odstraněním řádku v DataTable řádku a odebráním řádku. Když zavoláte metodu nebo RemoveAt metoduRemove, řádek se okamžitě odebere. Všechny odpovídající řádky v back-endovém zdroji dat nebudou ovlivněny, pokud pak předáte nebo zavoláte DataAdapter a zavoláte Update.DataSet DataTable Když použijete metodu Delete , zůstane řádek v DataTable řádku a je označen k odstranění. Pokud pak předáte DataTable nebo DataSet zavoláte DataAdapter a zavoláte Update, odstraní se odpovídající řádek v back-endovém zdroji dat.

Pokud se DataTable mapy mapují nebo generují z jedné databázové tabulky, můžete využít výhod DbCommandBuilder objektu k automatickému vygenerování objektu DeleteCommand, InsertCommanda UpdateCommand objektů pro objekty DataAdapter. Další informace naleznete v tématu Generování příkazů pomocí CommandBuilders.

Mapování hodnot na datovou sadu pomocí UpdatedRowSource

Pomocí vlastnosti objektu můžete určit, jak se hodnoty vrácené ze zdroje dat mapují zpět na DataTable následující volání metody DataAdapterUpdatedRowSource Update objektuDbCommand. UpdatedRowSource Nastavením vlastnosti na jednu z hodnot výčtu UpdateRowSource můžete řídit, zda jsou výstupní parametry vrácené DataAdapter příkazy ignorovány nebo použity na změněný řádek v objektu DataSet. Můžete také určit, zda je první vrácený řádek (pokud existuje) použit na změněný řádek v sadě DataTable.

Následující tabulka popisuje různé hodnoty výčtu UpdateRowSource a jejich vliv na chování příkazu použitého s parametrem DataAdapter.

UpdatedRowSource – výčet Popis
Both Výstupní parametry i první řádek vrácené sady výsledků mohou být namapovány na změněný řádek v sadě DataSetvýsledků .
FirstReturnedRecord Na změněný řádek v DataSetsadě výsledků můžou být namapována pouze data v prvním řádku vrácené sady výsledků .
None Všechny výstupní parametry nebo řádky vrácené sady výsledků se ignorují.
OutputParameters Na změněný řádek v nástroji DataSet. lze mapovat pouze výstupní parametry.

Metoda Update vyřeší vaše změny zpět ke zdroji dat; ostatní klienti však možná změnili data ve zdroji dat od posledního vyplnění DataSet. Pokud chcete aktualizovat DataSet aktuální data, použijte metodu a Fill metoduDataAdapter. Do tabulky se přidají nové řádky a aktualizované informace se začlení do existujících řádků. Metoda Fill určuje, zda bude přidán nový řádek nebo existující řádek bude aktualizován prozkoumáním hodnot primárního klíče řádků v DataSet řádcích a řádků vrácených SelectCommand. Fill Pokud metoda narazí na hodnotu primárního klíče pro řádek, DataSet který odpovídá hodnotě primárního klíče z řádku ve výsledcích vrácených objektem SelectCommand, aktualizuje existující řádek informacemi z řádku vráceného řádkem SelectCommand a nastaví RowState existující řádek na Unchanged. Pokud řádek vrácený SelectCommand primárním klíčem má hodnotu primárního klíče, která neodpovídá žádné hodnotě primárního klíče řádků v DataSetsadě , Fill metoda přidá nový řádek s RowState hodnotou Unchanged.

Poznámka:

SelectCommand Pokud vrátí výsledky vnějšího spojení, DataAdapter nenastaví PrimaryKey hodnotu výsledného DataTablespojení . Musíte definovat PrimaryKey sami sebe, aby se zajistilo, že se správně přeloží duplicitní řádky. Další informace naleznete v tématu Definování primárních klíčů.

Pokud chcete zpracovat výjimky, ke kterým může dojít při volání Update metody, můžete pomocí RowUpdated události reagovat na chyby aktualizace řádků při jejich výskytu (viz Zpracování událostí dataAdapter), nebo můžete nastavit DataAdapter.ContinueUpdateOnError před true voláním Updatea odpovědět na informace o chybě uložené ve RowError vlastnosti konkrétního řádku po dokončení aktualizace (viz Informace o chybě řádku).

Poznámka:

Volání AcceptChanges na DataSet, DataTablenebo DataRow způsobí, že všechny Original hodnoty budou DataRow přepsány Current hodnotami pro hodnotu .DataRow Pokud byly hodnoty polí, které identifikují řádek jako jedinečné, změněny, po zavolání AcceptChanges Original hodnot již nebudou odpovídat hodnotám ve zdroji dat. AcceptChangesje volána automaticky pro každý řádek během volání metody Update metody .DataAdapter Při volání metody Update můžete zachovat původní hodnoty tak, že nejprve nastavíte AcceptChangesDuringUpdate vlastnost DataAdapter na hodnotu false, nebo vytvořením obslužné rutiny události pro RowUpdated událost a nastavením Status na SkipCurrentRowhodnotu . Další informace naleznete v tématu Sloučení obsahu datové sady a zpracování událostí dataAdapter.

Příklad

Následující příklady ukazují, jak provádět aktualizace upravených řádků tím, že explicitně nastavíte UpdateCommand DataAdapter a zavoláte jeho Update metodu. Všimněte si, že parametr zadaný v klauzuli WHERE příkazu UPDATE je nastaven tak, aby používal Original hodnotu SourceColumn. To je důležité, protože Current hodnota mohla být změněna a nemusí odpovídat hodnotě ve zdroji dat. Hodnota Original je hodnota, která byla použita k naplnění DataTable ze zdroje dat.

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

Sloupce automatického přírůstku

Pokud tabulky z vašeho zdroje dat mají automaticky inkrementované sloupce, můžete sloupce vyplnit tak, že vrátíte DataSet hodnotu automatického přírůstku jako výstupní parametr uložené procedury a namapujete ji na sloupec v tabulce tak, že vrátíte hodnotu automatického přírůstku v prvním řádku sady výsledků vrácenou uloženou procedurou nebo příkazem SQL. nebo pomocí RowUpdated události spuštění dalšího DataAdapter příkazu SELECT. Další informace a příklad najdete v tématu Načítání hodnot identity nebo automatického číslování.

Řazení vložení, aktualizací a odstranění

V mnoha případech je důležité pořadí, v jakém se změny provedené prostřednictvím DataSet zdroje dat odesílají. Pokud se například aktualizuje hodnota primárního klíče pro existující řádek a přidá se nový řádek s hodnotou nového primárního klíče jako cizí klíč, je důležité aktualizaci zpracovat před vložením.

Můžete použít metodu Select DataTable vrácení DataRow pole, které odkazuje pouze na řádky s konkrétním RowState. Vrácenou matici Update pak můžete předat DataRow metodě zpracování upravených DataAdapter řádků. Zadáním podmnožiny řádků, které se mají aktualizovat, můžete řídit pořadí, ve kterém se vložení, aktualizace a odstranění zpracovávají.

Například následující kód zajistí, že se odstraněné řádky tabulky zpracovávají jako první, pak aktualizované řádky a potom vložené řádky.

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

Použití datovéhoadapteru k načtení a aktualizaci dat

DataAdapter můžete použít k načtení a aktualizaci dat.

  • Ukázka používá DataAdapter.AcceptChangesDuringFill ke klonování dat v databázi. Pokud je vlastnost nastavena jako false, AcceptChanges není volána při vyplňování tabulky a nově přidané řádky jsou považovány za vložené řádky. Ukázka proto tyto řádky používá k vložení nových řádků do databáze.

  • Ukázky používají DataAdapter.TableMappings k definování mapování mezi zdrojovou tabulkou a tabulkou DataTable.

  • Ukázka používá DataAdapter.FillLoadOption k určení, jak adaptér vyplní DataTable z DbDataReader. Při vytváření tabulky DataTable můžete zapisovat pouze data z databáze do aktuální verze nebo původní verze nastavením vlastnosti LoadOption.Upsert nebo LoadOption.PreserveChanges.

  • Ukázka také aktualizuje tabulku pomocí DbDataAdapter.UpdateBatchSize k provádění dávkových operací.

Před kompilací a spuštěním ukázky je potřeba vytvořit ukázkovou databázi:

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

Viz také