Język

Aktualizowanie kolumn typu zdefiniowanego przez użytkownika (UDT) za pomocą elementów DataAdapters

Dotyczy:programu SQL Server

Typy zdefiniowane przez użytkownika (UDT) można pobierać i modyfikować przy użyciu obiektu System.Data.DataSet i Microsoft.Data.SqlClient.SqlDataAdapter.

Przykłady kodu w tym artykule używają elementu Microsoft.Data.SqlClient, który jest dostępny jako pakiet NuGet. Aby dodać tę zależność do projektu, uruchom następujące polecenie:

dotnet add package Microsoft.Data.SqlClient

Wypełnianie zestawu danych

Użyj instrukcji SELECT Transact-SQL, aby wybrać wartości kolumn UDT w celu wypełnienia zestawu danych przy użyciu karty danych. W poniższym przykładzie przyjęto założenie, że masz tabelę Points zdefiniowaną z następującą strukturą i przykładowymi danymi. Poniższe instrukcje Transact-SQL tworzą tabelę Points i wstawią kilka wierszy.

CREATE TABLE dbo.Points
(
    id INT PRIMARY KEY,
    p Point
);

INSERT INTO dbo.Points
VALUES (1, CONVERT (Point, '1,3'));

INSERT INTO dbo.Points
VALUES (2, CONVERT (Point, '2,4'));

INSERT INTO dbo.Points
VALUES (3, CONVERT (Point, '3,5'));

INSERT INTO dbo.Points
VALUES (4, CONVERT (Point, '4,6'));
GO

Poniższy fragment kodu ADO.NET pobiera prawidłowe parametry połączenia, tworzy nowe SqlDataAdapteri wypełnia System.Data.DataTable wierszami danych z tabeli Points.

SqlDataAdapter da = new SqlDataAdapter(
   "SELECT id, p FROM dbo.Points", connectionString);
DataTable datTable = new DataTable("Points");
da.Fill(datTable);

Aktualizowanie danych UDT w zestawie danych

Za pomocą dwóch metod można zaktualizować kolumnę UDT w DataSet:

  • Podaj niestandardowe obiekty InsertCommand, UpdateCommandi DeleteCommand dla obiektu SqlDataAdapter.

  • Użyj konstruktora poleceń (Microsoft.Data.SqlClient.SqlCommandBuilder), aby automatycznie utworzyć polecenia INSERT, UPDATEi DELETE. Aby włączyć wykrywanie konfliktów, dodaj kolumnę sygnatury czasowej (alias rowversion) do tabeli programu SQL Server, która zawiera funkcję UDT. Sygnatura czasowa typ danych umożliwia oznaczenie wersji wierszy w tabeli i gwarantuje unikatowość w bazie danych. Po zmianie wartości w tabeli program SQL Server automatycznie aktualizuje 8-bajtowy numer binarny dla wiersza, którego dotyczy zmiana.

SqlCommandBuilder nie uwzględnia funkcji UDT do wykrywania konfliktów, chyba że w tabeli bazowej znajduje się sygnatura czasowa. Trasy zdefiniowane przez użytkownika mogą być porównywalne, dlatego nie są uwzględniane w klauzuli WHERE, gdy opcja "porównaj oryginalne wartości" jest używana do generowania polecenia.

Przykład

Poniższy przykład wymaga utworzenia drugiej tabeli zawierającej kolumnę udT Point i sygnaturę czasową. Obie tabele służą do zilustrowania sposobu tworzenia niestandardowych obiektów poleceń w celu aktualizowania danych oraz sposobu aktualizowania przy użyciu sygnatury czasowej kolumny. Uruchom następujące instrukcje Transact-SQL, aby utworzyć drugą tabelę i wypełnić ją przykładowymi danymi.

CREATE TABLE dbo.Points_ts
(
    id INT PRIMARY KEY,
    p Point,
    ts TIMESTAMP
);

INSERT INTO dbo.Points_ts (id, p)
VALUES (1, CONVERT (Point, '1,3'));

INSERT INTO dbo.Points_ts (id, p)
VALUES (2, CONVERT (Point, '2,4'));

INSERT INTO dbo.Points_ts (id, p)
VALUES (3, CONVERT (Point, '3,5'));

INSERT INTO dbo.Points_ts (id, p)
VALUES (4, CONVERT (Point, '4,6'));

Poniższy przykład ADO.NET ma dwie metody:

  • UserProvidedCommands, który demonstruje sposób podawania InsertCommandobiektów , UpdateCommandi DeleteCommand do aktualizowania Point udT w Points tabeli (bez kolumny znacznika czasu ).

  • CommandBuilder, który pokazuje, jak używać SqlCommandBuilder w tabeli Points_ts zawierającej sygnaturę czasową kolumny.

using System;
using System.Data;
using Microsoft.Data.SqlClient;

class Class1
{
// Retrieves the connection string
    private string connString = GetConnectionString();

    static void Main()
    {
        UserProvidedCommands();
        CommandBuilder();
    }

    static void UserProvidedCommands()
    {
        // Create a new SqlDataAdapter
        SqlDataAdapter da = new SqlDataAdapter(
            "SELECT id, p FROM dbo.Points", connString);

        // Setup the INSERT/UPDATE/DELETE commands
        SqlParameter idParam;
        SqlParameter pointParam;

        da.InsertCommand = new SqlCommand(
            "INSERT INTO dbo.Points (id, p) VALUES (@id, @p)",
            da.SelectCommand.Connection);
        idParam =
            da.InsertCommand.Parameters.Add("@id", SqlDbType.Int);
        idParam.SourceColumn = "id";
        pointParam =
            da.InsertCommand.Parameters.Add("@p", SqlDbType.Udt);
        pointParam.SourceColumn = "p";
        pointParam.UdtTypeName = "dbo.Point";

        da.UpdateCommand = new SqlCommand(
            "UPDATE dbo.Points SET p = @p WHERE id = @id",
            da.SelectCommand.Connection);
        idParam =
            da.UpdateCommand.Parameters.Add("@id", SqlDbType.Int);
        idParam.SourceColumn = "id";
        pointParam =
            da.UpdateCommand.Parameters.Add("@p", SqlDbType.Udt);
        pointParam.SourceColumn = "p";
        pointParam.UdtTypeName = "dbo.Point";

        da.DeleteCommand = new SqlCommand(
            "DELETE dbo.Points WHERE id = @id",
            da.SelectCommand.Connection);
        idParam =
            da.DeleteCommand.Parameters.Add("@id", SqlDbType.Int);
        idParam.SourceColumn = "id";

        // Fill the DataTable with UDT rows
        DataTable datTable = new DataTable("Points");
        da.Fill(datTable);

        // Display the contents of the p (Point) column
        foreach (DataRow r in datTable.Rows)
        {
            Point p = (Point)r[1];
            Console.WriteLine(
                "ID: {0}, x={1}, y={1}", r[0], p.X, p.Y);
        }

        // Update a row if the DataTable has at least 1 row
        if (datTable.Rows.Count > 0)
        {
            Point oldPoint = (Point)datTable.Rows[0][1];
            datTable.Rows[0][1] =
                new Point(oldPoint.X + 1, oldPoint.Y + 1);
        }

        // Delete the last row
        if (datTable.Rows.Count > 0)
        {
            // If we have at least 1 row
            datTable.Rows[1].Delete();
        }

        // Insert a row. This will fail if run twice
        // because 100 is a primary key value.
        datTable.Rows.Add(100, new Point(100, 200));

        // Send the changes back to the database
        da.Update(datTable);
    }

    static void CommandBuilder()
    {
        // Create a new SqlDataAdapter
        SqlDataAdapter da = new SqlDataAdapter(
            "SELECT id, ts, p FROM dbo.Points_ts", connString);

        // Select a few rows with UDTs from the database
        DataTable datTable = new DataTable("Points");
        da.Fill(datTable);

        // Display the contents of the p (Point) column
        foreach (DataRow r in datTable.Rows)
        {
            Point p = (Point)r[2];
            Console.WriteLine(
                "ID: {0}, x={1}, y={1}", r[0], p.X, p.Y);
        }

        // Update a row if DataTable has at least 1 row
        if (datTable.Rows.Count > 0)
        {
            Point oldPoint = (Point)datTable.Rows[0][2];
            datTable.Rows[0][2] =
                new Point(oldPoint.X + 1, oldPoint.Y + 1);
        }

        // Delete the last row
        if (datTable.Rows.Count > 0)
        {
            // if we have at least 1 row
            datTable.Rows[1].Delete();
        }

        // Insert a row. This will fail if run twice
        // because 100 is a primary key value
        datTable.Rows.Add(100, null, new Point(100, 200));

        // Use the CommandBuilder to generate DML statements
        SqlCommandBuilder bld = new SqlCommandBuilder(da);
        bld.ConflictDetection = ConflictOptions.CompareRowVersion;

        // Send the changes back to the database
        da.Update(datTable);
    }

    static private string GetConnectionString()
    {
        // To avoid storing the connection string in your code,
        // you can retrieve it from a configuration file.
        return "Data Source=localhost;Initial Catalog=AdventureWorks2022;"
               + "Integrated Security=SSPI";
    }
}