Newsgroup: it.comp.appl.access
Titolo: transazioni e recordset

Ciao
sto provando ad usare le transazioni per annullare le modifiche fatto ad una
sottomaschera, il problema è che uso una procedura come questa per far
definire la transazione
   Dim db As DAO.Database
    Dim qdf As DAO.QueryDef
    Dim rst As DAO.Recordset
    Dim prm As DAO.Parameter
    Dim strSource As String
    Set mwks = DBEngine.CreateWorkspace("mwks", "Admin", "")
    Set db = mwks.OpenDatabase(CurrentDb.name)
    Set qdf = db.QueryDefs(adhcSource)
    For Each prm In qdf.Parameters
        prm.Value = Eval(
prm.name
)
    Next prm
    Me.Painting = False
    Set rst = qdf.OpenRecordset
    rst.LockEdits = False
    Set SubDettaglioFatture.Form.Recordset = rst
    Me.Painting = True
    mwks.BeginTrans
    mfInTrans = True
    faccio partire la transazione quando premo il pulsante modifica con
questo codice
    Call ResetData
    ora il problema nasce quando decido di annullare la transazione con il
seguente codice
       If mfInTrans Then
         mwks.Rollback
         mfInTrans = False
         Set mwks = Nothing
      End If
    vorrei riportare la situazione ante transazione ma non so come fare in
particolare come fare a riportare la proprietà recordset della sottomaschera
alla situazione  naturale,  con i dati provenienti dalla query di origine.
Ciao
Grazie

 ha scritto nel messaggio
news:9f55o0$8oc$1...@serv1.iunet.it...



Provo a descrivere meglio il problema ho una maschera con sottomaschera
Fatture/DettaglioFatture che normalmente è solo in lettura vorrei permettere
la modifica dei dati tramite un pulsante modifica che però gestisca le
transazioni in modo che, se a metà della modifica si cambia idea premendo
annulla vengano annullate tutte le modifiche della sottomaschera e non solo
l'ultima.(utilizzando il rollback)
Se invece si decide di salvare vengono salvate tutte le modifiche
(utilizzando il CommitTrans)
in un caso e nell'altro la maschera deve tornare in sola lettura ed è a
questo punto che nascono i problemi, la sottomaschera sembra perdere i
riferimenti e non si aggiorna più (la gestione della sincronizzazione
Maschera/sottomaschera è fatta manualmente e non utilizzando collega campi
secondari e master) quasi che non riuscisse più a recuperare il recordset di
partenza.
Inoltre la somma che è messa nel piè di pagina maschera che viene
visualizzata da una textbox nella maschera principale che di solito funzione
quando esco da una transazione mi segnale #errore.
Ciao
Grazie
Steve


Quello che scrivi e' sostanzialmente corretto, non perche' non si possa fare
ma perche' non sono pensate per questo, lokkano molte risorse mentre
l'utente modifica lentamente i dati.
Ci eravamo posti questo problema e in effetti transazioni che annullano
modifiche su maschere e sottomaschere su cui agisce l'utente si possono
fare, ho il codice se qualcuno lo desidera. Ho anche postato un mdb di
esempio al webmaster del sitocomune ma da quasi un mese non mi ha ancora
risposto... :-(
Piuttosto se sono coinvolte modifiche alle sottomaschere da annullare si
puo' creare con ado una copia dei dati in un recordset non associato (li
chiamano "recordset out of thin air"), associare quella alla sottomaschera e
poi si puo' aggiornare o meno il database alla fine. Questo dovrebbe
comportarsi molto bene, IMHO. Se interessa questa soluzione, se ne puo'
parlare.
Ciao! Luca


Ciao
sono interessato al codice per annullare le modifiche alle sottomaschere se
me lo puoi postare te ne sarei grato, č molto interessante anche l'ipotesi
di usare ado e i recordset non associati, vorrei capire qual'č il metodo piů
efficace, possiamo parlarne?
ciao
steve

 risposto... :-(
Come funziona? tanto per farmi un'idea...
Grassie :-
Max

 efficace, possiamo parlarne?
Volentieri. Per quello che ho capito, la Micro$oft ha fornito una soluzione
al problema (il che mostra che e' un problema, al contrario di quello che ho
sentito dire altrove) nel database di esempio che puoi scaricare anche dal
sito comune.
La loro soluzione potrebbe essere perfetta per te, dovresti dargli
un'occhiata. Sfrutta un meccanismo molto semplice : crea una tabella
temporanea e ci copia tutta la tabella. Se alla fine sono accettate le
modifiche la ricopia nell'originale, altrimenti la butta semplicemente.
Io purtroppo non l'ho potuta usare per un sacco di motivi (ho dei dubbi sul
suo comportamento con molti utenti, uso gli .adp e quindi le tabelle le
dovrei creare sul server...) percio' mi sono immaginato per prima cosa una
soluzione che usi le transazioni (che ti ho spedito come mi hai chiesto) e
poi sfrutti i meccanismi di rollback e commit, che sembrano fatti apposta.
L'unica limitazione per ora trovata e' se nella maschera principale ci sono
i pulsanti di spostamento, ma ho un'idea per poter avere anche questi.
Il metodo comunque consiste in questo : all'interno di un workspace separato
da quello base (altrimenti il rollback della transazione annullerebbe anche
altre maschere) apro una transazione e creo due recordset, uno per la
maschera principale e uno per la sottomaschera. I due recordset vengono poi
assegnati a Me.recordset per le due maschere (quello che si dice maschere
non associate o unbound). A questo punto in chiusura posso decidere come far
terminare la transazione. Come ti dicevo ci sono varie considerazioni : una
transazione fa il lock delle risorse, ovvero impedisce ad altri utenti di
accedervi durante la digitazione (e questo puo' essere visto come bene o
male a seconda dei casi e delle esigenze). Inoltre non si possono usare i
normali meccanismi per collegare le sottomaschere alle maschere (i campi
master e child, per intenderci) perche' altrimenti access forza delle
transazioni separate che aggiorna continuamente. Percio' il collegamento va
fatto "a mano", anche se non e' una cosa complicata (come vedi
dall'esempio).
Nota che questa soluzione usa DAO perche' volevo realizzare un .adp, pero'
e' possibile creare anche la cosa in ADO per un .ADP o in generale per un
server remoto.
L'altra possibilita' sarebbe creare un recordset per la maschera principale
e uno per la sottomaschera, pero' non "popolarli" con una query direttamente
ma ricopiarli da altri due recordset, questi si' associati alla query. Se ti
chiarisce meglio e' la stessa differenza tra il passaggio per valore o per
riferimento dei parametri di una funzione.
La ricopiatura avviene creando i recordset "destinazione" vuoti e dentro un
ciclo occorre aggiungere nuovi record e riempirli con i valori presi dai
recordset pieni. Questo si puo' sicuramente fare in ADO ma non so in DAO.
Speravo di poter usare recordset.clone ma vengono aggiornate anche le copie.
L'utente modifica cosi' le copie e non gli originali e puoi scegliere tu se
aggiornare (con una ricopiatura in senso opposto) i dati nelle tabelle in
chiusura o semplicemente buttare tutto.
I vantaggi sono che si puo' sfruttare la cache molto bene, si possono
ricopiare solo alcuni dati ma soprattutto non si pasticcia con le tabelle
temporanee e si lavora solo sui client.
Come ultima possibilita' mi stavo chiedendo se sarebbe possibile usare il
provider ADO msdatashape per ottenere una copia dei dati.
Non ho ancora trovato il tempo di tentare una soluzione ma spero di poterlo
fare presto, se vuoi buttartici tu...
Che ne pensi?
Ciao, Luca

 Max
Luca

cut

Ti ringrazio dell'esposizione molto interessante che vedo solo ora perchè
ero fuori ufficio, (ora sto scrivendo da casa) ieri sono riuscito a far
funzionare le transazioni su maschera e sottomaschera e domani provo a
postare la "mia" soluzione che per altro riprende modificandola quanto
proposto dal libro "Manuale di Programmazione di Access 2000" della Jackson,
in pratica quando devo modificare un record apro un'altra maschera in
modalità dialog e gli passo come openargs (in questo caso) il numero fattura
che utilizzo nell'evento open della nuova maschera per sincronizzare il
record su quello che voglio modificare, questa maschera non è collegata e
setto la proprietà recordset ad un recordset che ho creato utilizzando un
altro workspace poi sempre, nell'evento open, inizio la transazione, ho poi
un pulsante Salva e uno Annulla che provvedono a dare il commit o il
rollback e chiudono la maschera facendomi rientrare nella maschera di
partenza che dopo una requery viene sincronizzata con il record di partenza,
qui sono sorti dei problemi perchè passati 4 o 5 secondi access andava in
errore dicendo oggetto non valido e tutti i campi davano #errore, ho dovuto
separare completamente le query di partenza ed inoltre verificare che
fossero avvenute delle variazioni al record prima di dare il rollback solo
così l'errore è scomparso e ora funziona tutto egregiamente.
Proverò sicuramente quanto proponi per vedere se il tutto funziona meglio e
farò sapere.
Ciao
Grazie

 terminare la transazione.
Quindi, se ho capito bene: crei un workspaces separato, ma solo per isolare
la transazione sui soli recordset creati nel nuovo workspaces.
Inizia la transazione, crei e  popoli i recordset, li assegni alle maschere.
Modifichi i dati sulle maschere, e in chiusura scegli se dare commit o
rollback.
Non capisco una cosa... se apro una maschera basata su una query, le
transazioni non funzionano.
Il discriminante che fa funzionare le transazioni è l'assegnare ad una
maschera un recordset che è stato aperto via codice quando una transazione
era già iniziata?
Ma il recordset come deve essere? una cosa che esiste solo in memoria?
Oppure può essere generato con un  CurrentDB.openrecordset("SELECT......
FROM MiaTabella.....")?
E con un  CurrentDB.openrecordset("MiaQuery")?
E' quindi possibile fare una transazione che annulli le modifiche ai dati,
apportate (tramite i controlli associati di una maschera) ai dati di una
tabella del mio DB (generando opportunamente il recordset sorgente)?
Però c'è una cosa che non capisco.... da prove pratiche ho verificato che:
se inizio una transazione, modifico un dato in una tabella con una query di
aggiornamento, poi leggo il dato con un'altra query e lo uso per fare altre
cose (sempre prima di terminare la transazione) leggo il dato vecchio, cioè
quello presente PRIMA dell'inizio della transazione.
Per poter leggere il dato aggiornato devo prima terminare la transazione.
Queste questioni come si riflettono sul discorso fatto sopra? nella
fattispecie mi chiedo.... sono nella situazione descritta sopra ed ho un
recordset con alcune righe, assegnato ad una maschera, e creato dopo aver
iniziato una transazione; tramite la maschera modifico una riga, poi mi
sposto sulla riga seguente e faccio altre modifiche... se poi torno sulla
prima riga che dati vedo? quelli originali? o quelli modificati?
Mi sto incasinando..... :-)
Max


provo a postare la procedura che citavo ieri.
Option Compare Database
Option Explicit
Public OKToClose As Boolean
Private mwks As DAO.Workspace
Private mfInTrans As Boolean
Private Const adhcSource _
As String = "QryModDettaglioFatture"
Private Sub CboRagioneSociale_AfterUpdate()
    Me.TxtEmessaIl.SetFocus
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
    If KeyCode = vbKeyPageDown Or KeyCode = vbKeyPageUp Then
        KeyCode = 0
    End If
End Sub
Private Sub Form_Load()
     OKToClose = False
End Sub
Private Sub Form_Open(Cancel As Integer)
   Call ResetData
   Sincro Me, "IdFattura", OpenArgs
   Me.TxtRagioneSociale.SetFocus
End Sub
Private Sub PstSalva_Click()
On Error GoTo Err_PstSalva_Click
    Dim risposta As VbMsgBoxResult
    If IsNull(Me.RagioneSociale) Then
        MsgBox ("Devi inserire una Ragione sociale")
        Me.TxtRagioneSociale.SetFocus
    Else
        risposta = MsgBox("Sei sicuro di voler" & (Chr(13)) & "
salvare?", vbCritical + vbYesNo, "Salva?")
        If risposta = vbYes Then
            If Me.IDComune = 0 Then
                Me.IDComune = "8358"
            End If
            If mfInTrans Then
                mwks.CommitTrans
                mfInTrans = False
            End If
            Set mwks = Nothing
            DoCmd.RunCommand acCmdSaveRecord
            OKToClose = True
            DoCmd.Close
        End If
    End If
Exit_PstSalva_Click:
    Exit Sub
Err_PstSalva_Click:
    MsgBox Err.Description
    Resume Exit_PstSalva_Click
End Sub
Private Sub PstAnnulla_Click()
On Error GoTo Err_PstAnnulla_Click
    Dim risposta As VbMsgBoxResult
    risposta = MsgBox("Sei sicuro di voler" + vbNewLine + "    annullare?",
vbCritical + vbYesNo, "Annulla?")
    If risposta = vbYes Then
        If Me.Dirty Or Me.SubModDettaglioFatture.Form.Dirty Then
             If mfInTrans Then
                mwks.Rollback
                mfInTrans = False
             End If
            Me.Undo
        End If
        Set mwks = Nothing
        OKToClose = True
        DoCmd.Close
     End If
Exit_PstAnnulla_Click:
    Exit Sub
Err_PstAnnulla_Click:
    MsgBox Err.Description
    Resume Exit_PstAnnulla_Click
End Sub
Private Sub ResetData()
    Dim db As DAO.Database
    Dim qdf As DAO.QueryDef
    Dim rst As DAO.Recordset
    Dim prm As DAO.Parameter
    Dim strSource As String
   ' Crea e assegna un recordset filtrato alla sottomaschera.
    Set mwks = DBEngine.CreateWorkspace("mwks", "Admin", "")
    Set db = mwks.OpenDatabase(CurrentDb.name)
    Set qdf = db.QueryDefs(adhcSource)
    For Each prm In qdf.Parameters
        prm.Value = Eval(
prm.name
)
    Next prm
    Me.Painting = False
    Set rst = qdf.OpenRecordset
    rst.LockEdits = False
    Set SubModDettaglioFatture.Form.Recordset = rst
    Me.Painting = True
    'Start a new subform transaction
    mwks.BeginTrans
    mfInTrans = True
End Sub
il tutto funziona egregiamente, per quel che riguarda l'utilizzo dei dati
prima della fine della transazione finchè non dai il commit gli stessi non
vengono scritti nella tabella e quindi leggi i dati vecchi, non so se è
possibile copiare i dati del recordset utilizzato nella transazione per
poter fare delle operazioni con questi dati, il problema penso sia riuscire
a puntarli.
continuerò a provare
ciao
Steve

 Max
Luca

 effetti mi hai fatto riflettere su alcune cosette a cui non avevo pensato.
Ora mi sono abbastanza scasinato :-) Grassie.
Max