24 messaggi dal 20 giugno 2005
Buongiorno a tutti,
ho una View che utilizza due tabelle (testa e dettaglio tipico documento come una fattura) e in fase di update o insert, ho questa sequenza di operazioni:

A ) Registro la tabella testa e recupero il suo ID;
B ) Registro la tabella dettaglio aggiungendo l'ID recuperato

Voglio inserire il controllo su Rowversion della seconda tabella, quindi prima del punto A faccio un controllo del Rowversion delle righe, se qualcuna è stata variata, ritorno alla View per visualizzare la variazione, aggiorno la Rowversion ,

se non c'è nessuna variazione, proseguo con il punto A e B.
Ora, se mentre io eseguo il punto A e qualcuno mi ha variato un dato della mia tabelle 2, con il punto B, andrei a sovrascrivere i dati dell'utente concorrente, come posso essere sicuro che ciò non avvenga?

La verifica della Rowversion è più conveniente farla fare al Controller MVC o demandarla a Sequel Server ?

Saluti.
Sebastiano
497 messaggi dal 08 febbraio 2009
Ciao

Dall'esempio risulta evidente che devi gestire la concorrenza, pertanto per avere una certa sicurezza nel risultato è bene lavorare con le transazioni.
Queste sono a livello di database, ovvero è lui che ti garantisce il corretto funzionamento della concorrenza (mi fermo qui, è un discorso lungo e delicato ma presumo tu lo conosca).


Nel tuo caso dovresti fare qualcosa del genere:
- Avvii la transazione
- Aggiorni A verificando il row version (UPDATE A SET ... WHERE ID = .. AND ROW-VERSION = ...)
- Se il numero dei record aggiornato è 1, allora prosegui con gli step successivi. Se è 0 vuol dire che A è cambiato nel frattempo. Fai la rollback e gestisci come preferisci il fatto che A sia cambiato nel mentre
- A quel punto fai tutti gli aggiornamenti su B verificando sempre il row version (update simile a quella di A)
- Se il numero dei record aggiornati corrisponde al numero che ti aspettavi, allora fai la commit: tutto è andato bene. In caso contrario, sai che qualcuno ha modificato almeno una riga, per cui fai la rollback e gestisci la concorrenza come preferisci.


Per quanto riguarda il fatto di farlo da codice o da DB dipende da diversi fattori, per cui non c'è una verità assoluta.
Di sicuro se hai diversi programmi scritti con linguaggi diversi che devono fare le stesse cose, allora ti conviene fare una stored-procedure sul DB e richiamare quella.

Se invece lo fai solo dal tuo programma, allora scriverla sul DB o sul codice è questione di gusti e comunque molto soggettivo
24 messaggi dal 20 giugno 2005
Ciao,
innanzi tutto grazie per la risposta così veloce, e comunque chiarito il problema.
Per quanto riguarda il punto A riesco a gestirlo io, invece per il punto B dovrei fare qualcosa del genere:

public async Task<bool> UpdQtaArticoloAsync(string segno, double qta,string key01,string campo)
{
FormattableString sql = $@"BEGIN TRANSACTION;
SELECT * FROM MAG_ARTICOLI WITH (UpdLock) WHERE KEY01 ='{key01}' AND ROWVERSION ='{rowversion}';
UPDATE MAG_ARTICOLI SET {campo} = {campo} {segno} {qta} WHERE KEY01 ='{key01}';

SELECT * FROM MAG_ARTICOLI WITH (UpdLock) WHERE KEY01 ='{key02}' AND ROWVERSION ='{rowversion2}';
UPDATE MAG_ARTICOLI SET {campo} = {campo2} {segno2} {qta2} WHERE KEY01 ='{key02}';

se UPDATE tutti i record
COMMIT TRANSACTION
ritorna TRUE
altrimenti
fai il rollback
ritorna FALSE"
bool result = await db.QueryScalarAsync<bool>(sql);
return result;
}
permettimi di fare anche qualche errore di codice, ma la logica che dovrei usare è questa? Ho aggiunto alla prima select, la seconda per simulare il punto B con due record da aggiornare. A questo punto è indifferente se creo la stored da codice e la invio al db o creo la stored sul db e la richiamo da codice.

Saluti.
Sebastiano
497 messaggi dal 08 febbraio 2009
Ciao.

Dipende molto da come accedi al DB, quanto conosci SQL, ...
Ti giro un esempio semplice da capire, anche se non è il migliore a livello di performance (fai una query per ogni riga da aggiornare):


using (var connection = new OleDbConnection(...))
{
   connection.Open();
   using (var transaction = connection.BeginTransaction())
   {
      try
      {
         var query = "UPDATE MAG_ARTICOLI SET CAMPO = @campo, SEGNO = @segno WHERE ID = @id AND ROWVERSION = @rowversion";
         using (var command = new OleDbCommand(query, connection))
         {
            foreach (var riga in RigheDaAggiornare)
            {
               command.Parameters.Clear();
               command.Parameters.Add(new OleDbParameter("@campo", <valoreCampo>);
               command.Parameters.Add(new OleDbParameter("@segno", <valoreSegno>);
               command.Parameters.Add(new OleDbParameter("@id", <valoreId>);
               command.Parameters.Add(new OleDbParameter("@rowversion", <valoreRowversion>);
               var righeAggiornate = command.ExecuteNonQuery();
               if (righeAggiornate != 1)
               {
                   // Non ha aggiornato 1 riga come ci si aspettava, problema di concomitanza!
                  throw new MyConcurrencyException(<datiCheTiServono>);
               }
            }
            // Tutte le righe aggiornate correttamente, committo
            transaction.Commit();
         }
      }
      catch 
      {
         transaction.Rollback();
         throw;
      }
   }
}




Anche io ho buttato giù il codice al volo, possono esserci errori.
Vedi te poi come preferisci fare, l'importante è capirne il flusso
24 messaggi dal 20 giugno 2005
Ciao,
siamo arrivati allo stesso punto. Mi spiego, il codice che mi hai scritto è simile (potrei anche dire uguale) a quello che uso per creare le stringhe da inviare al db, devo però aggiunger il rollback, ma a prescindere da tutto ho trovato la soluzione al mio problema e devo solo decidere se farla da codice cosi come fa il codice che mi hai scritto o farlo fare a sequel server spostando il tutto su una stored, ma ripeto concettualmente il problema è risolto. Se non ti creo disturbo appena risolvo vorrei condividere la soluzione.

Saluti.
Sebastiano

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.