Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V prostředí s více uživateli existují dva modely pro aktualizaci dat v databázi: optimistická souběžnost a pesimistické souběžnost. Objekt DataSet je navržený tak, aby podporoval použití optimistické souběžnosti pro dlouhotrvající aktivity, jako je vzdálený přenos dat a interakce s daty.
Pesimistická synchronizace spočíva v uzamčení řádků v datovém zdroji, aby ostatní uživatelé nemohli upravit data, což by ovlivnilo stávajícího uživatele. Když uživatel v pesimistickém modelu provede akci, která způsobí použití zámku, nemůžou ostatní uživatelé provádět akce, které by byly v konfliktu se zámkem, dokud ho vlastník zámku neuvolní. Tento model se primárně používá v prostředích, kde dochází k velkému kolizí dat, takže náklady na ochranu dat pomocí zámků jsou menší než náklady na vrácení transakcí zpět, pokud dojde ke konfliktům souběžnosti.
Proto v pesimistickém modelu souběžnosti uživatel, který aktualizuje řádek, vytvoří zámek. Dokud uživatel nedokončí aktualizaci a uvolní zámek, nikdo jiný nemůže tento řádek změnit. Z tohoto důvodu je pesimistická konkurence nejlépe implementována, když budou časy uzamčení krátké, jako při programovém zpracování záznamů. Pesimistická souběžnost nejedná se o škálovatelnou možnost, když uživatelé pracují s daty a způsobují uzamčení záznamů po relativně dlouhou dobu.
Poznámka:
Pokud potřebujete aktualizovat více řádků ve stejné operaci, vytvoření transakce je škálovatelnější možností než použití pesimistické uzamčení.
Naproti tomu uživatelé, kteří využívají optimistickou souběžnost, nezamknou řádek při čtení. Když chce uživatel aktualizovat řádek, musí aplikace určit, jestli jiný uživatel změnil řádek od jeho čtení. Optimistická souběžnost se obecně používá v prostředích s nízkou úrovní konkurence o data. Optimistická souběžnost zvyšuje výkon, protože není vyžadováno žádné zamykání záznamů a uzamčení záznamů vyžaduje další prostředky serveru. K udržování zámků záznamů se také vyžaduje trvalé připojení k databázovému serveru. Protože v optimistickém modelu souběžnosti tomu tak není, jsou připojení k serveru schopná obsloužit větší počet klientů za kratší dobu.
V modelu optimistické souběžnosti se považuje za porušení, ke kterému došlo, pokud uživatel po přijetí hodnoty z databáze změní hodnotu předtím, než se první uživatel pokusí ji upravit. Jak server vyřeší porušení souběžnosti, je nejlepší ukázat nejprve popisem následujícího příkladu.
Následující tabulky sledují příklad optimistické souběžnosti.
V 1:00 p.m., User1 přečte řádek z databáze s následujícími hodnotami:
ID zákazníka Příjmení Jméno
101 Smith Bob
| Název sloupce | Původní hodnota | Aktuální hodnota | Hodnota v databázi |
|---|---|---|---|
| ID zákazníka | 101 | 101 | 101 |
| Příjmení | Smith | Smith | Smith |
| Křestní jméno | Robert | Robert | Robert |
V 1:01 p.m., User2 přečte stejný řádek.
V 1:03 p.m. změní User2 FirstName z "Bob" na "Robert" a aktualizuje databázi.
| Název sloupce | Původní hodnota | Aktuální hodnota | Hodnota v databázi |
|---|---|---|---|
| ID zákazníka | 101 | 101 | 101 |
| Příjmení | Smith | Smith | Smith |
| Křestní jméno | Robert | Robert | Robert |
Aktualizace bude úspěšná, protože hodnoty v databázi v době aktualizace odpovídají původním hodnotám, které má Uživatel2.
Ve 13:05 User1 změní Bobovo jméno na "James" a pokusí se aktualizovat řádku.
| Název sloupce | Původní hodnota | Aktuální hodnota | Hodnota v databázi |
|---|---|---|---|
| ID zákazníka | 101 | 101 | 101 |
| Příjmení | Smith | Smith | Smith |
| Křestní jméno | Robert | Jakub | Robert |
V tuto chvíli uživatel User1 narazí na optimistické porušení souběžnosti, protože hodnota v databázi (Robert) už neodpovídá původní hodnotě, kterou Uživatel1 očekával (Bob). Porušení souběžnosti jednoduše informuje, že aktualizace selhala. Rozhodnutí teď musí být provedeno, zda chcete přepsat změny poskytnuté uživatelem User2 změnami zadanými uživatelem User1, nebo zrušit změny uživatelem User1.
Testování porušení optimistické souběžnosti
Existuje několik technik pro testování porušení optimistické souběžnosti. Jedním z nich je zahrnutí sloupce časového razítka do tabulky. Databáze běžně poskytují funkce časového razítka, které lze použít k identifikaci data a času poslední aktualizace záznamu. Pomocí této techniky se do definice tabulky zahrne sloupec časového razítka. Při každé aktualizaci záznamu se časové razítko aktualizuje tak, aby odráželo aktuální datum a čas. V testu pro porušení optimistické souběžnosti se vrátí sloupec časového razítka s libovolným dotazem na obsah tabulky. Při pokusu o aktualizaci se hodnota časového razítka v databázi porovná s původní hodnotou časového razítka obsaženou v upraveném řádku. Pokud se shodují, provede se aktualizace a sloupec časového razítka se aktualizuje aktuálním časem tak, aby odrážel aktualizaci. Pokud se neshodují, došlo k narušení optimistické souběžnosti.
Další technikou pro testování porušení optimistické souběžnosti je ověření, že všechny původní hodnoty sloupců v řádku stále odpovídají hodnotám nalezeným v databázi. Zvažte například následující dotaz:
SELECT Col1, Col2, Col3 FROM Table1
Pokud chcete otestovat porušení optimistické souběžnosti při aktualizaci řádku v tabulce Table1, vydáte následující příkaz UPDATE:
UPDATE Table1 Set Col1 = @NewCol1Value,
Set Col2 = @NewCol2Value,
Set Col3 = @NewCol3Value
WHERE Col1 = @OldCol1Value AND
Col2 = @OldCol2Value AND
Col3 = @OldCol3Value
Pokud původní hodnoty odpovídají hodnotám v databázi, provede se aktualizace. Pokud byla hodnota změněna, aktualizace neupraví řádek, protože klauzule WHERE nenajde shodu.
Upozorňujeme, že v dotazu se doporučuje vždy vrátit jedinečnou hodnotu primárního klíče. V opačném případě může předchozí příkaz UPDATE aktualizovat více než jeden řádek, což nemusí být váš záměr.
Pokud sloupec ve zdroji dat umožňuje hodnoty null, možná budete muset rozšířit klauzuli WHERE, abyste zkontrolovali odpovídající odkaz na hodnotu null v místní tabulce a ve zdroji dat. Například následující příkaz UPDATE ověří, že odkaz null v místním řádku stále odpovídá nulovému odkazu ve zdroji dat nebo že hodnota v místním řádku stále odpovídá hodnotě ve zdroji dat.
UPDATE Table1 Set Col1 = @NewVal1
WHERE (@OldVal1 IS NULL AND Col1 IS NULL) OR Col1 = @OldVal1
Při použití modelu optimistické souběžnosti se také můžete rozhodnout použít méně omezující kritéria. Například použití pouze sloupců primárního klíče v klauzuli WHERE způsobí přepsání dat bez ohledu na to, jestli byly ostatní sloupce aktualizovány od posledního dotazu. Klauzuli WHERE můžete také použít pouze u konkrétních sloupců, což vede k přepsání dat, pokud se od posledního dotazování neaktualizovala konkrétní pole.
Událost DataAdapter.RowUpdated
Událost RowUpdated objektu DataAdapter lze použít ve spojení s dříve popsanými technikami k poskytnutí oznámení vaší aplikaci o porušení optimistické souběžnosti.
RowUpdated nastane po každém pokusu Modified o aktualizaci řádku z datové sady. Díky tomu můžete přidat speciální kód pro zpracování, včetně zpracování, když dojde k výjimce, přidání vlastních informací o chybách, přidání logiky opakování atd. Objekt RowUpdatedEventArgs vrátí RecordsAffected vlastnost obsahující počet řádků ovlivněných konkrétním příkazem aktualizace pro upravený řádek v tabulce. Když nastavíte příkaz update tak, aby testoval optimistickou souběžnost, RecordsAffected vrátí tato vlastnost v důsledku toho hodnotu 0, když dojde k porušení optimistické souběžnosti, protože nebyly aktualizovány žádné záznamy. V takovém případě dojde k výjimce. Událost RowUpdated umožňuje zpracovat tento výskyt a vyhnout se výjimce nastavením příslušné hodnoty RowUpdatedEventArgs.Status , například UpdateStatus.SkipCurrentRow. Další informace o RowUpdated události naleznete v tématu Zpracování událostí dataAdapter.
Volitelně můžete nastavit DataAdapter.ContinueUpdateOnError na true, před voláním aktualizace a odpovědět na informace o chybě uložené ve RowError vlastnosti konkrétního řádku po Update dokončení. Další informace naleznete v sekci Informace o chybě řádku.
Příklad optimistické souběžnosti
Následuje jednoduchý příklad, který nastaví UpdateCommand objektu DataAdapter pro testování optimistické souběžnosti, a poté pomocí události RowUpdated otestuje porušení optimistické souběžnosti. Když dojde k porušení optimistické souběžnosti, aplikace nastaví RowError pro řádek, na který byla aktualizace zaměřena, aby odrážela porušení optimistické souběžnosti.
Všimněte si, že hodnoty parametrů předané klauzuli WHERE příkazu UPDATE jsou mapovány na Original hodnoty příslušných sloupců.
' Assumes connection is a valid SqlConnection.
Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID", _
connection)
' The Update command checks for optimistic concurrency violations
' in the WHERE clause.
adapter.UpdateCommand = New SqlCommand("UPDATE Customers " &
"(CustomerID, CompanyName) VALUES(@CustomerID, @CompanyName) " & _
"WHERE CustomerID = @oldCustomerID AND CompanyName = " &
"@oldCompanyName", connection)
adapter.UpdateCommand.Parameters.Add( _
"@CustomerID", SqlDbType.NChar, 5, "CustomerID")
adapter.UpdateCommand.Parameters.Add( _
"@CompanyName", SqlDbType.NVarChar, 30, "CompanyName")
' Pass the original values to the WHERE clause parameters.
Dim parameter As SqlParameter = adapter.UpdateCommand.Parameters.Add( _
"@oldCustomerID", SqlDbType.NChar, 5, "CustomerID")
parameter.SourceVersion = DataRowVersion.Original
parameter = adapter.UpdateCommand.Parameters.Add( _
"@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName")
parameter.SourceVersion = DataRowVersion.Original
' Add the RowUpdated event handler.
AddHandler adapter.RowUpdated, New SqlRowUpdatedEventHandler( _
AddressOf OnRowUpdated)
Dim dataSet As DataSet = New DataSet()
adapter.Fill(dataSet, "Customers")
' Modify the DataSet contents.
adapter.Update(dataSet, "Customers")
Dim dataRow As DataRow
For Each dataRow In dataSet.Tables("Customers").Rows
If dataRow.HasErrors Then
Console.WriteLine(dataRow (0) & vbCrLf & dataRow.RowError)
End If
Next
Private Shared Sub OnRowUpdated( _
sender As object, args As SqlRowUpdatedEventArgs)
If args.RecordsAffected = 0
args.Row.RowError = "Optimistic Concurrency Violation!"
args.Status = UpdateStatus.SkipCurrentRow
End If
End Sub
// Assumes connection is a valid SqlConnection.
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID",
connection);
// The Update command checks for optimistic concurrency violations
// in the WHERE clause.
adapter.UpdateCommand = new SqlCommand("UPDATE Customers Set CustomerID = @CustomerID, CompanyName = @CompanyName " +
"WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName", connection);
adapter.UpdateCommand.Parameters.Add(
"@CustomerID", SqlDbType.NChar, 5, "CustomerID");
adapter.UpdateCommand.Parameters.Add(
"@CompanyName", SqlDbType.NVarChar, 30, "CompanyName");
// Pass the original values to the WHERE clause parameters.
SqlParameter parameter = adapter.UpdateCommand.Parameters.Add(
"@oldCustomerID", SqlDbType.NChar, 5, "CustomerID");
parameter.SourceVersion = DataRowVersion.Original;
parameter = adapter.UpdateCommand.Parameters.Add(
"@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName");
parameter.SourceVersion = DataRowVersion.Original;
// Add the RowUpdated event handler.
adapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet, "Customers");
// Modify the DataSet contents.
adapter.Update(dataSet, "Customers");
foreach (DataRow dataRow in dataSet.Tables["Customers"].Rows)
{
if (dataRow.HasErrors)
Console.WriteLine(dataRow [0] + "\n" + dataRow.RowError);
}
protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args)
{
if (args.RecordsAffected == 0)
{
args.Row.RowError = "Optimistic Concurrency Violation Encountered";
args.Status = UpdateStatus.SkipCurrentRow;
}
}