Gestione dei lock dei record generalizzata

In una applicazione gestionale c’è sempre l’esigenza di gestire le modifiche contemporanee su di un record da parte di più utenti e con Instant Developer Foundation lo possiamo fare in modo semplice.

Il nostro scopo è fare in modo che se un record è in stato di modifica, quindi un utente ha modificato almeno un suo campo o un campo di un record figlio gli altri utenti non possano effettuare a loro volta modifiche ma vengano informati della situazione con un messaggio a video.

Per risolvere questo problema di solito occorre scrivere molto codice ma Instant Developer Foundation ci viene in aiuto con il Document Helper la classe che viene utilizzata per rendere globali gli eventi dei documenti, un paio di eventi e l’attivazione della proprietà Document lock sul documento.

Quindi la gestione dei lock posso attivarla solo sui documenti per i quali mi interessa farlo.

Gli eventi che ci interessano sono due:

  • GetLock - prende un lock su un documento
  • ReleaseLock - rilascia un lock su un documento

Oltre a questo ci occorre una tabella dove memorizzare i lock attivi, che sarà composta da questi campi:

  • Id - la pk del record
  • DNADoc - il DNA del documento su cui si è preso il lock
  • DataOra - la data e ora del lock
  • UtenteLock - l’utente che ha preso il lock

Cos’è il DNA di un documento? È il suo identificativo univoco che contiene i valori della chiave primaria il nome del documento e in quale componete sta.
Per esempio la stringa "CommonDoc$Order",10248 indica il documento Order del componente CommonDoc con chiave 10248.

L’evento GetLock viene notificato non appena un utente da un pannello modifica un campo del documento presente o di uno dei suoi documenti figli.

In questo evento mi viene passato il documento come IDDocument e quindi posso prelevare il suo DNA e verificare se nella tabella dei lock è presente un record di lock di un utente che non è quello loggato cosi da poterlo segnalare a video.
Per farlo devo impostare a false il parametro di output Result e utilizzare il parametro LockInfo per indicare il messaggio che verrà portato in automatico dal framework sulla videata.

Nel caso che non sia presente un lock dovrò scrivere un record nella tabella dei lock.

Per i lock che rimangono appesi perché non è stato completato il ciclo di modifica mediante salvataggio o annullamento del lock, perché la sessione è scaduta, si può aggiungere una verifica sulla data e ora di presa del lock e gestire e definire quale intervallo di tempo si considera per mantenere il lock.

Qui di seguito il codice dell’evento GetLock.

event DocHelper.GetLock(
  IDDocument Doc 
  string UserInfo
  inout string LockInfo 
  inout boolean Result
)
{
  Lock l = Lock.checkLock(Doc.getDNA())
  boolean creaLock = false
  //  
  // Nessuno ha un lock
  if (l == null)
  {
    creaLock = true
    Result = true
  }
  else  // Esiste un lock
  {
    // Il lock è preso dallo stesso utente allora aggiorno l'orario ad adesso
    if (l.UtenteLock == CommonDoc.userName)
    {
      l.DataOra = now()
      l.saveToDB(...)
      Result = true
    }
    else  // 
    {
      float minuti = (now() - l.DataOra) * 24 * 60
      //
      // Considero il lock obsoleto se sono passati 30 minuti dal suo inizio
      if (minuti > CommonDoc.TempoDurataLock)
      {
        l.deleted = true
        l.saveToDB(...)
        creaLock = true
        Result = true
      }
      else  // Il documento è ancora in lock e lo comunico
      {
        LockInfo = formatMessage("Il documento risulta bloccato dall'utente |1 - Non puoi apportare modifiche (riprova più tardi ricaricando i dati)", l.UtenteLock, ...)
        Result = false
      }
    }
  }
  //
  // Creo il lock
  if (creaLock)
  {
    Lock newLock = new()
    newLock.init()
    newLock.DNADoc = Doc.getDNA()
    newLock.DataOra = now()
    newLock.UtenteLock = CommonDoc.userName
    newLock.saveToDB(...)
  }
}

Per rilasciare il lock viene notificato l’evento ReleaseLock dove cancelliamo il record dalla tabella dei lock. Questo il suo codice:

event DocHelper.ReleaseLock(
  IDDocument Doc
)
{
  Lock l = new()
  l.DNADoc = Doc.getDNA()
  // 
  try 
  {
    l.loadFromDB(...)
    l.deleted = true
    l.saveToDB(...)
  }
  catch 
  {
    l = null
  }
}

Cosa ne pensate?
Potente vero?

Vi allego un esempio funzionante che realizza la cosa

Gestione Lock.zip (1,6 MB)

2 Mi Piace

Questo è molto interessante. C’è qualcosa di simile anche per Instant Developer Cloud?

@giorba per il momento no.
Instant Developer Foundation ha un framework con 24 anni di sviluppo e ci sono comportamenti implementati che su Cloud non ci sono.

La gestione dei documenti nei pannelli di Foundation ha degli automatismi attivabili che su Cloud non abbiamo.

Penso che si possano realizzare anche in autonomia comportamenti globali creando un tuo documento base, che eredita dai Document, e che implementi eventi globalizzati.

2 Mi Piace

Noi lo gestiamo in modo diverso: abbiamo un campo sul record con un docID, questo viene aggiornato SEMPRE in modo generalizzato durante il salvataggio (tutte le tabelle devono avere il campo).
Al momento del salvataggio inoltre viene letto il valore in DB di quel campo relativo al record che si sta modificando, se il docID coincide allora nessuno ha toccato quel record, altrimenti viene bloccato il salvataggio e visualizzato il messaggio che “un altro utente ha modificato il record”.

Questo ovviamente a differenza della procedura descritta da Paolo non blocca il record per tutti gli altri (stile teamworks, ossia “ci sto lavorando io, nessuno deve toccarlo”) ma ne blocca il salvataggio solo se viene rilevato che è stato modificato (lock ottimistico).

2 Mi Piace

@d.termini approccio interessante il vostro e questo lo si può usare sia su Cloud che Foundation senza problemi.

Ti avviso che qualcuno ha già modificato il record prima di te e la tua immagine che hai a video è diversa da quella che hai caricato prima delle tue modifiche.

2 Mi Piace