Delen via


Gegevensbronnen bijwerken met DataAdapters

Van toepassing op: .NET Framework .NET Standard

ADO.NET downloaden

De Update methode van de DataAdapter wordt aangeroepen om wijzigingen van een DataSet terug naar de gegevensbron op te lossen. De Update-methode, net als de Fill-methode, neemt als argumenten een exemplaar van een DataSet, en een optioneel DataTable-object of een optionele DataTable-naam. Het DataSet exemplaar is de DataSet die de aangebrachte wijzigingen bevat, en de DataTable identificeert de tabel waaruit de wijzigingen moeten worden opgehaald. Als er geen DataTable is opgegeven, wordt de eerste DataTable in het DataSet gebruikt.

Wanneer u de Update methode aanroept, analyseert de DataAdapter wijzigingen die zijn aangebracht en voert u de juiste opdracht uit (INSERT, UPDATE of DELETE). Wanneer DataAdapter een wijziging in een DataRow tegenkomt, gebruikt het de InsertCommand, UpdateCommand, of DeleteCommand om de wijziging te verwerken.

Met deze eigenschappen kunt u de prestaties van uw ADO.NET toepassing maximaliseren door de opdrachtsyntaxis op te geven tijdens het ontwerp en, indien mogelijk, door gebruik te maken van opgeslagen procedures. U moet de opdrachten expliciet instellen voordat u aanroept Update. Als Update wordt aangeroepen en de juiste opdracht niet bestaat voor een bepaalde update (bijvoorbeeld geen DeleteCommand voor verwijderde rijen), wordt er een uitzondering gesmeten.

Belangrijk

Als u opgeslagen SQL Server-procedures gebruikt om gegevens te bewerken of verwijderen met behulp van een DataAdapter, moet u ervoor zorgen dat u niet gebruikt SET NOCOUNT ON in de definitie van de opgeslagen procedure. Dit zorgt ervoor dat het aantal geretourneerde rijen nul is, wat door de DataAdapter als een gelijktijdigheidsconflict wordt geïnterpreteerd. In dit geval wordt er een DBConcurrencyException gegooid.

Opdrachtparameters kunnen worden gebruikt om invoer- en uitvoerwaarden op te geven voor een SQL-instructie of opgeslagen procedure voor elke gewijzigde rij in een DataSet. Zie DataAdapter-parameters voor meer informatie.

Opmerking

Het is belangrijk om het verschil te begrijpen tussen het verwijderen van een rij in een DataTable en het wissen van de rij. Wanneer u de Remove of RemoveAt methode aanroept, wordt de rij onmiddellijk verwijderd. Alle bijbehorende rijen in de gegevensbron van de back-end worden niet beïnvloed als u vervolgens de DataTable of DataSet aan een DataAdapter doorgeeft en Update aanroept. Wanneer u de Delete methode gebruikt, blijft de rij in de DataTable rij staan en wordt deze gemarkeerd voor verwijdering. Als u vervolgens de DataTable of DataSet aan DataAdapter doorgeeft en Update aanroept, wordt de bijbehorende rij in de back-end gegevensbron verwijderd.

Als uw DataTable in kaart brengt of gegenereerd wordt vanuit een enkele databasetabel, kunt u profiteren van het DbCommandBuilder object om voor de DataAdapter tabel automatisch de DeleteCommand, InsertCommand en UpdateCommand objecten te genereren. Zie Opdrachten genereren met CommandBuilders voor meer informatie.

UpdatedRowSource gebruiken om waarden toe te wijzen aan een DataSet

U kunt bepalen hoe de waarden die worden geretourneerd uit de gegevensbron worden toegewezen aan de DataTable na een aanroep van de Update methode van een DataAdapter, door de UpdatedRowSource eigenschap van een SqlCommand object te gebruiken. Door de UpdatedRowSource eigenschap in te stellen op een van de UpdateRowSource opsommingswaarden, kunt u bepalen of uitvoerparameters die door de DataAdapter opdrachten worden geretourneerd, worden genegeerd of toegepast op de gewijzigde rij in de DataSet. U kunt ook opgeven of de eerste geretourneerde rij (als deze bestaat) wordt toegepast op de gewijzigde rij in de DataTable.

In de volgende tabel worden de verschillende waarden van de UpdateRowSource opsomming beschreven en hoe deze van invloed zijn op het gedrag van een opdracht die wordt gebruikt met een DataAdapter.

Opsomming VanRowSource bijgewerkt Description
Both Zowel de uitvoerparameters als de eerste rij van een geretourneerde resultatenset kunnen worden toegewezen aan de gewijzigde rij in de DataSet.
FirstReturnedRecord Alleen de gegevens in de eerste rij van een geretourneerde resultatenset kunnen worden toegewezen aan de gewijzigde rij in de DataSet.
None Uitvoerparameters of rijen van een geretourneerde resultatenset worden genegeerd.
OutputParameters Alleen uitvoerparameters kunnen worden toegewezen aan de gewijzigde rij in de DataSet.

De methode lost uw wijzigingen weer op in de gegevensbron. Andere Update clients hebben mogelijk gegevens gewijzigd in de gegevensbron sinds de laatste keer dat u de DataSetgegevens hebt ingevuld. Als u uw DataSet gegevens wilt vernieuwen met de huidige gegevens, gebruikt u de DataAdapter en Fill methode. Nieuwe rijen worden toegevoegd aan de tabel en bijgewerkte informatie wordt opgenomen in bestaande rijen.

De Fill methode bepaalt of een nieuwe rij wordt toegevoegd of een bestaande rij wordt bijgewerkt door de primaire-sleutelwaarden van de rijen in de DataSet rijen te onderzoeken en de rijen die worden geretourneerd door de SelectCommand. Als de Fill methode een primaire sleutelwaarde tegenkomt voor een rij in de DataSet die overeenkomt met een primaire sleutelwaarde uit een rij in de resultaten die door de SelectCommand worden geretourneerd, dan wordt de bestaande rij bijgewerkt met de informatie uit de rij die door de SelectCommand wordt geretourneerd en stelt de RowState van de bestaande rij in op Unchanged. Als een rij die door de SelectCommand rij wordt geretourneerd, een primaire-sleutelwaarde heeft die niet overeenkomt met een van de primaire-sleutelwaarden van de rijen in de DataSet, voegt de Fill methode een nieuwe rij toe met een RowState van Unchanged.

Opmerking

Als de SelectCommand de resultaten van een OUTER JOIN retourneert, zal er geen PrimaryKey-waarde worden ingesteld voor het resulterende DataTable. U moet de PrimaryKey zelf definiëren om ervoor te zorgen dat dubbel aanwezige rijen correct worden opgelost.

Als u uitzonderingen wilt verwerken die zich kunnen voordoen bij het aanroepen van de Update methode, kunt u de RowUpdated gebeurtenis gebruiken om te reageren op fouten bij het bijwerken van rijen wanneer deze optreden (zie DataAdapter-gebeurtenissen verwerken), of kunt u deze instellen ContinueUpdateOnErrortrue voordat u aanroept Updateen reageert op de foutgegevens die zijn opgeslagen in de RowError eigenschap van een bepaalde rij wanneer de update is voltooid.

Opmerking

Het aanroepen AcceptChanges van de DataSet, DataTable, of DataRow zorgt ervoor dat alle Original waarden voor een DataRow worden overschreven met de Current waarden voor de DataRow. Als de veldwaarden die de rij identificeren als uniek zijn gewijzigd, komen de waarden na het aanroepen AcceptChanges van de Original waarden niet meer overeen met de waarden in de gegevensbron. AcceptChanges wordt automatisch aangeroepen voor elke rij tijdens een aanroep naar de Update methode van een DataAdapter. U kunt de oorspronkelijke waarden behouden tijdens een aanroep van de Update-methode door eerst de AcceptChangesDuringUpdate-eigenschap van de DataAdapter op false in te stellen, of door een eventhandler voor het RowUpdated-event te maken en de Status-eigenschap in te stellen op SkipCurrentRow. Zie DataAdapter-gebeurtenissen verwerken voor meer informatie.

In de volgende voorbeelden ziet u hoe u updates kunt uitvoeren voor gewijzigde rijen door expliciet de UpdateCommand van een DataAdapter in te stellen en de Update methode aan te roepen.

Opmerking

De parameter die is opgegeven in de WHERE clause wordt UPDATE statement ingesteld om de Original waarde van de SourceColumn te gebruiken. Dit is belangrijk omdat de Current waarde mogelijk is gewijzigd en mogelijk niet overeenkomt met de waarde in de gegevensbron. De Original waarde is de waarde die is gebruikt om de DataTable gegevensbron te vullen.

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

Kolommen met autoincrement

Als de tabellen uit uw gegevensbron automatisch incrementele kolommen bevatten, kunt u de kolommen in uw DataSet kolom vullen door de waarde voor automatisch verhogen als uitvoerparameter van een opgeslagen procedure te retourneren en deze toe te voegen aan een kolom in een tabel door de waarde voor automatisch verhogen in de eerste rij van een resultatenset te retourneren die wordt geretourneerd door een opgeslagen procedure of SQL-instructie, of door de RowUpdated gebeurtenis van de DataAdapter gebeurtenis te gebruiken om een extra SELECT-instructie uit te voeren. Zie Waarden voor identiteit of autonummering ophalen voor meer informatie en een voorbeeld.

Volgorde van invoegingen, updates en verwijderingen

In veel gevallen is de volgorde waarin wijzigingen via de DataSet naar de gegevensbron worden gestuurd, belangrijk. Als een primaire-sleutelwaarde voor een bestaande rij bijvoorbeeld wordt bijgewerkt en er een nieuwe rij is toegevoegd met de nieuwe primaire-sleutelwaarde als refererende sleutel, is het belangrijk om de update vóór de invoegbewerking te verwerken.

U kunt de Select methode van de DataTable functie gebruiken om een DataRow matrix te retourneren die alleen verwijst naar rijen met een bepaalde RowStatewaarde. Vervolgens kunt u de geretourneerde DataRow matrix doorgeven aan de Update methode van de DataAdapter om de gewijzigde rijen te verwerken. Door een subset van rijen op te geven die moeten worden bijgewerkt, kunt u de volgorde bepalen waarin invoegingen, updates en verwijderingen worden verwerkt.

Example

De volgende code zorgt er bijvoorbeeld voor dat de verwijderde rijen van de tabel eerst worden verwerkt, vervolgens de bijgewerkte rijen en vervolgens de ingevoegde rijen.

// Assumes that dataSet and adapter are valid objects.
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));

Een DataAdapter gebruiken om gegevens op te halen en bij te werken

U kunt een DataAdapter gebruiken om de gegevens op te halen en bij te werken.

  • Het voorbeeld gebruikt DataAdapter.AcceptChangesDuringFill om de gegevens in de database te klonen. Als de eigenschap is ingesteld als onwaar, wordt AcceptChanges niet aangeroepen bij het vullen van de tabel en worden de zojuist toegevoegde rijen behandeld als ingevoegde rijen. In het voorbeeld worden deze rijen dus gebruikt om de nieuwe rijen in de database in te voegen.

  • De voorbeelden gebruiken DataAdapter.TableMappings om de toewijzing tussen de brontabel en DataTable te definiëren.

  • In het voorbeeld wordt gebruikt DataAdapter.FillLoadOption om te bepalen hoe de adapter de DataTable van dbDataReader vult. Wanneer u een gegevenstabel maakt, kunt u alleen de gegevens schrijven van de database naar de huidige versie of de oorspronkelijke versie door de eigenschap in te stellen als LoadOption.Upsert of LoadOption.PreserveChanges.

  • In het voorbeeld wordt ook de tabel bijgewerkt door DbDataAdapter.UpdateBatchSize te gebruiken voor het uitvoeren van batchbewerkingen.

Voordat u het voorbeeld compileert en uitvoert, moet u de voorbeelddatabase maken:

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 Microsoft.Data.SqlClient;
using System.Linq;
using CSDataAdapterOperations.Properties;

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

namespace CSDataAdapterOperations.Properties
{
    internal sealed partial class Settings : System.Configuration.ApplicationSettingsBase
    {
        private static readonly Settings defaultInstance =
            ((Settings)(System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

        public static Settings Default => defaultInstance;

        [System.Configuration.ApplicationScopedSetting()]
        [System.Configuration.DefaultSettingValue("Data Source=(local);Initial Catalog=MySchool;Integrated Security=True")]
        public string MySchoolConnectionString => ((string)(this["MySchoolConnectionString"]));
    }
}

Zie ook