Megosztás a következőn keresztül:


Optimista párhuzamosság

Többfelhasználós környezetben két modell létezik az adatbázisok adatainak frissítésére: optimista egyidejűség és pesszimista egyidejűség. Az DataSet objektum célja, hogy ösztönözze az optimista egyidejűség használatát a hosszú ideig futó tevékenységekhez, például az adatok újraformálásához és az adatokkal való interakcióhoz.

A pesszimista egyidejűség magában foglalja az adatforrás sorainak zárolását, hogy más felhasználók ne módosíthassák az adatokat az aktuális felhasználót érintő módon. Pesszimista modellben, amikor egy felhasználó olyan műveletet hajt végre, amely egy zárolás alkalmazását okozza, más felhasználók nem hajthatnak végre olyan műveleteket, amelyek ütköznének a zárolással, amíg a zárolás tulajdonosa fel nem oldja azt. Ezt a modellt elsősorban olyan környezetekben használják, ahol nagy a versengés az adatokért, így az adatok zárolással történő védelmének költsége alacsonyabb, mint az egyidejűségi ütközések esetén a tranzakciók visszagördülésének költsége.

Ezért egy pesszimista egyidejűségi modellben egy sor frissítését végző felhasználó létrehoz egy zárolást. Amíg a felhasználó nem fejezte be a frissítést, és nem oldotta fel a zárolást, senki más nem módosíthatja ezt a sort. Ezért a pesszimista egyidejűség akkor a legjobb, ha a zárolási idők rövidek lesznek, mint a rekordok programozott feldolgozása során. A pesszimista egyidejűség nem skálázható lehetőség, ha a felhasználók az adatokkal kommunikálnak, és a rekordokat viszonylag nagy ideig zárolják.

Feljegyzés

Ha több sort kell frissítenie ugyanabban a műveletben, akkor a tranzakció létrehozása méretezhetőbb lehetőség, mint pesszimista zárolás használata.

Ezzel szemben az optimista egyidejűséget használó felhasználók nem zárolnak egy sort olvasáskor. Amikor egy felhasználó frissíteni szeretne egy sort, az alkalmazásnak meg kell határoznia, hogy egy másik felhasználó módosította-e a sort az olvasás óta. Az optimista egyidejűséget általában olyan környezetekben használják, amelyben alacsony az adatok versengése. Az optimista egyidejűség javítja a teljesítményt, mivel nincs szükség rekordok zárolására, és a rekordok zárolásához további kiszolgálói erőforrásokra van szükség. Emellett a rekordzárolások fenntartása érdekében állandó kapcsolatra van szükség az adatbázis-kiszolgálóval. Mivel egy optimista egyidejűségi modellben ez nem így van, a kiszolgálóhoz való kapcsolatok kevesebb idő alatt nagyobb számú ügyfelet szolgálhatnak ki.

Optimista egyidejűségi modell esetén a szabálysértés akkor minősül szabálysértésnek, ha miután egy felhasználó kapott egy értéket az adatbázisból, egy másik felhasználó módosítja az értéket, mielőtt az első felhasználó megkísérelte volna módosítani. Az alábbi példa első leírásával legjobban az látható, hogy a kiszolgáló hogyan oldja meg az egyidejűség megsértését.

Az alábbi táblázatok az optimista egyidejűség példáját követik.

13:00-kor a User1 beolvassa az adatbázisból a következő értékeket:

CustID vezetéknév utóneve

101 Smith Bob

Oszlop neve Eredeti érték Aktuális érték Érték az adatbázisban
CustID 101 101 101
LastName Smith Smith Smith
FirstName Róbert Róbert Róbert

13:01-kor a User2 ugyanazt a sort olvassa be.

13:03-kor a User2 "Bob" névről "Robert" névre módosítja a FirstName nevet, és frissíti az adatbázist.

Oszlop neve Eredeti érték Aktuális érték Érték az adatbázisban
CustID 101 101 101
LastName Smith Smith Smith
FirstName Róbert Robert Róbert

A frissítés sikeres, mert az adatbázisban a frissítés időpontjában lévő értékek megegyeznek a User2 eredeti értékeivel.

13:05-kor az 1. felhasználó "Bob" utónevét "James" névre módosítja, és megpróbálja frissíteni a sort.

Oszlop neve Eredeti érték Aktuális érték Érték az adatbázisban
CustID 101 101 101
LastName Smith Smith Smith
FirstName Róbert James Robert

Ezen a ponton az 1. felhasználó optimista egyidejűségi hibát tapasztal, mivel az adatbázisban lévő érték ("Robert") már nem egyezik meg az 1. felhasználó által várt eredeti értékkel ("Bob"). Az egyidejűség megsértése egyszerűen tudatja Önvel, hogy a frissítés sikertelen volt. Most már el kell dönteni, hogy felülírja-e a Felhasználó2 által megadott módosításokat a Felhasználó1 által megadott módosításokkal, vagy visszavonja-e a módosításokat a Felhasználó1 által.

Optimista egyidejűségi szabálysértések tesztelése

Az optimista egyidejűség megsértésének tesztelésére számos módszer létezik. Ehhez bele kell foglalni egy időbélyeg-oszlopot a táblába. Az adatbázisok gyakran biztosítanak időbélyeg-funkciót, amely a rekord utolsó frissítésének dátumának és időpontjának azonosítására használható. Ezzel a módszerrel egy időbélyegoszlop szerepel a tábladefinícióban. A rekord frissítésekor az időbélyeg az aktuális dátumot és időpontot tükrözi. Az optimista egyidejűségi szabálysértések tesztelése során a rendszer visszaadja az időbélyeg oszlopot a tábla tartalmának bármely lekérdezésével. Frissítési kísérlet esetén az adatbázis időbélyeg-értékét a rendszer összehasonlítja a módosított sor eredeti időbélyeg-értékével. Ha egyeznek, a rendszer végrehajtja a frissítést, és az időbélyeg oszlop az aktuális időponttal frissül, hogy tükrözze a frissítést. Ha nem egyeznek, optimista egyidejűségi szabálysértés történt.

Az optimista egyidejűség megsértésének tesztelésére szolgáló másik módszer annak ellenőrzése, hogy a sor összes eredeti oszlopértéke megegyezik-e az adatbázisban található értékekkel. Lásd példaként az alábbi lekérdezést:

SELECT Col1, Col2, Col3 FROM Table1  

Ha optimista egyidejűségi szabálysértéseket szeretne tesztelni az 1. táblázat sorainak frissítésekor, a következő UPDATE utasítást kell kiadnia:

UPDATE Table1 Set Col1 = @NewCol1Value,  
              Set Col2 = @NewCol2Value,  
              Set Col3 = @NewCol3Value  
WHERE Col1 = @OldCol1Value AND  
      Col2 = @OldCol2Value AND  
      Col3 = @OldCol3Value  

Amíg az eredeti értékek megegyeznek az adatbázisban lévő értékekkel, a frissítés végrehajtása történik. Ha egy érték módosult, a frissítés nem módosítja a sort, mert a WHERE záradék nem talál egyezést.

Vegye figyelembe, hogy javasoljuk, hogy mindig adjon vissza egy egyedi elsődleges kulcsértéket a lekérdezésben. Ellenkező esetben a fenti UPDATE utasítás több sort is frissíthet, ami nem feltétlenül a szándéka.

Ha az adatforrás egy oszlopa engedélyezi a null értéket, előfordulhat, hogy ki kell terjesztenie a WHERE záradékot, hogy ellenőrizze a helyi táblában és az adatforrásban található megfelelő nullhivatkozást. Az alábbi UPDATE utasítás például azt ellenőrzi, hogy a helyi sorban lévő nullhivatkozások továbbra is egyeznek-e az adatforrás null hivatkozásával, vagy hogy a helyi sorban lévő érték továbbra is megegyezik-e az adatforrás értékével.

UPDATE Table1 Set Col1 = @NewVal1  
  WHERE (@OldVal1 IS NULL AND Col1 IS NULL) OR Col1 = @OldVal1  

Optimista egyidejűségi modell használata esetén is dönthet úgy, hogy kevésbé korlátozó feltételeket alkalmaz. Ha például csak az elsődleges kulcsoszlopokat használja a WHERE záradékban, az adatok felülíródnak, függetlenül attól, hogy a többi oszlop frissült-e az utolsó lekérdezés óta. A WHERE záradékot csak adott oszlopokra alkalmazhatja, így az adatok felülíródnak, kivéve, ha bizonyos mezők frissültek az utolsó lekérdezés óta.

A DataAdapter.RowUpdated esemény

Az objektum RowUpdated eseménye a DataAdapter korábban ismertetett technikákkal együtt használható az optimista egyidejűségi szabálysértések alkalmazásának értesítésére. A RowUpdated az adatkészlet módosított sorainak frissítésére tett minden kísérlet után következik be. Ez lehetővé teszi speciális kezelési kód hozzáadását, beleértve a kivétel bekövetkeztekor történő feldolgozást, az egyéni hibainformációk hozzáadását, az újrapróbálkozások logikáját stb. Az RowUpdatedEventArgs objektum egy RecordsAffected tulajdonságot ad vissza, amely a tábla módosított sorainak adott frissítési parancsa által érintett sorok számát tartalmazza. Ha a frissítési parancsot az optimista egyidejűség tesztelésére állítja be, a RecordsAffected tulajdonság ennek eredményeképpen 0 értéket ad vissza, ha optimista egyidejűségi szabálysértés történt, mert nem frissültek rekordok. Ha ez a helyzet, a rendszer kivételt jelez. A RowUpdated esemény lehetővé teszi, hogy kezelje ezt az eseményt, és elkerülje a kivételt egy megfelelő RowUpdatedEventArgs.Status érték( például UpdateStatus.SkipCurrentRow) beállításával. További információ a RowUpdated eseményről: DataAdapter-események kezelése.

Ha szeretné, a DataAdapter.ContinueUpdateOnError értéket true értékre állíthatja, mielőtt meghívja az Update-et, és válaszolhat egy adott sor RowError tulajdonságában tárolt hibainformációkra a frissítés befejezésekor. További információ: Sorhiba adatai.

Optimista egyidejűségi példa

Az alábbi egyszerű példa egy DataAdapter UpdateCommand elemét állítja be az optimista egyidejűség tesztelésére, majd a RowUpdated esemény használatával teszteli az optimista egyidejűségi szabálysértéseket. Optimista egyidejűségi szabálysértés esetén az alkalmazás beállítja annak a sornak a RowError értékét, amelyről a frissítés ki lett adva, hogy tükrözze az optimista egyidejűség megsértését.

Vegye figyelembe, hogy az UPDATE parancs WHERE záradékának átadott paraméterértékek a megfelelő oszlopok eredeti értékeire vannak leképezve.

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

Lásd még