Newsgroup: it.comp.lang.visual-basic
Titolo: Errore su Collection - non si riesce a recuperare il valore

Ciao a tutti
Ho un problemino con VB6
Imposto i riferimenti di Microsoft ADO
istanzio una collection a livello di form
Dim c As Collection
poi vado a popolare una combo prendendo dei valori da una tabella 
con il seguente codice
Sub fill(sconn As String)
    Set c = New Collection
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim sql As String
    sql = "select au_id, au_lname from authors "
 
    cn.Open sconn
    rs.Open sql, cn
    Dim i As Integer
    i = 0
    Do While Not rs.EOF
        Combo1.AddItem rs(1)
        Combo1.ItemData(Combo1.NewIndex) = i
        c.Add rs(0), CStr(i)
        i = i + 1
        rs.MoveNext
    Loop
 
End Sub
se faccio eseguire il codice in debug vedo che gli elementi della
collection sono caricati correttamente
es ? c.Item(2)  mi da il corrispondente dato '238-95-7766
se lo stesso codice lo faccio eseguire da un pulsante dopo che ho
caricato la combo ricevo l'errore 
MsgBox c.Item(2)
 
Errore di runtime '3420'
L'oggetto non è più valido
se popolo la collection con dei dati che NON provengono da un db
l'errore non avviene
es.
    Set c = Nothing
    Set c = New Collection
    Dim i As Integer
    For i = 0 To 10
        c.Add "aa" & CStr(i), CStr(i)
    Next i
CHe ne dite?
ciao e grazie Simona



trovato!!!!!!
invece che scrivere
c.Add rs(0), CStr(i)
bisogna esplicitare il valore del campo
c.Add rs(0).Value, CStr(i)
 
ciao ciao
Simona

 c.Add rs(0).Value, CStr(i)
Si abituati a farlo sempre, anzi io uso ultimamente 
Rs.Fields(0).Value, più prolisso ma secondo me migliore.


Mi permetto di aggiungere che ...
Rs.Fields(0).Value andrebbe sostituito con un più pratico
Rs.Fields("nomecampo").Value
pensa infatti cosa succederebbe se per un motivo o per un altro dovessi 
inserire non alla fine o eliminare un campo non finale degli elementi 
della select, ne usciresti pazzo.
Inserendo la chiave nominativa anzichè quella posizionale ti risparmi un 
bel po' di mal di testa :-)
ciao
stefano



ok ok
ho messo rs(0) solo per l'esempio in questione.....
però è stranto che durante il ciclo for next, l'assegnazione dell'rs
alla collection non mi da errore c.Add rs(0) e me lo da solo alla fine
quando tento di visualizzare un elemento della collection stessa.
mah
ciao e grazie a te e ad Andrea
Simona


Non è strano, è normale.
Stiamo parlando di catene di oggetti, e quando tu usi la sintassi
'rs(0)' ti affidi alle risoluzione automatica che fa il runtime di VB6
delle proprietà predefinite, perchè in realtà quello che vuoi leggere
è 'rs.Fields(0).Value'.
Ora, finchè assegni a variabili semplici, il runtime è sufficentemente
furbo e risolve correttamente, quando però metti in mezzo la
collection, che rappresenta un insieme di genericche istanze di tipo
Object, allora la cosa diventa ambigua; ecco quindi che l'istruzione
c.Add rs(0)
NON viene interpretata come 'aggiungi a c il valore del campo 0', come
sopra, ma correttamente come 'aggiungi a c l'oggetto Field che sta
nell'insieme Fields alla posizione 0' (bada che dico correttamente, il
runtime qui prende la scelta più congruente, e' proprio la sintassi
che è usata a sproposito)
Quindi cosa succede? che quando poi distruggi il Recordset, l'oggetto
Field dipendente viene invalidato, e quando lo recuperi dalla
collection ottieni l'errore che hai visto... ti è successo ora con una
collection ma ci sono tanti casi analoghi che potenzialmente possono
portare a questi problemi
Morale della storia: *NON* usare le property predefinite degli
oggetti, e vivi felice!! queste sintassi abbreviate sono un brutto
vizio che, agli inizi, penso tutti abbiamo preso, ma sono sempre da
evitare... tanto non è che risparmiamo carta, e con l'Intellisense poi
Visual Studio ce le scrive quasi da solo.
Non è un caso, per inciso, che siano sparite, con poche eccezioni,
da .NET

 bel po' di mal di testa :-)
Hai si i numeri non il uso mai, anche se devo ciclare i campi lo faccio con
un For Each AdoDBField in Rs.Fields di norma.

 un For Each AdoDBField in Rs.Fields di norma.
posso sapere a che ti serve?...per curiosità eh!
Qui si parla di VB6 .......SEI eh!
e di ADO ...(ADO eh non 
ado.net
)
Il recordset è l'oggetto principe da usare per non si incasinare!
Tra le altre cose prevede quella che poi correttamente verrà "esplosa
all'ennesima potenza" in net ossia una "tipizzazione forte" (è
estremamente scorretto in questo contesto ma rende l'idea della
chiarezza mentale che si produce nella mente dl programmatore)
Ossia
RS("DataRegistrazione")
RS("ImportoFattura")
RS("Nominativo")
contrapposto a rs.fields(0)
Tra le altre cose il recordset è di fatto una collezione...perchè
appesantirlo con un altra?
Quel for each quindi perchè?
non fa prima a chiamare direttamente il campo con il suo nome?
NO mi puoi rispondere....
perchè associo a runtime qualcosa ai campi...o proprio i
campi....quindi NON SO a prori come si chiama il mio field!
Altrimenti non vedo nessun altro scopo se non rallentare il tutto!
del tipo......
rs.open
if rs.eof then
   rs.close
   exit sub/function
endif
do
   textbox1.text=rs("Nominativo") & ""
   textbox2.text=format$(rs("dataRegistrazione), "dd/mm/yyyy")
   textbox3.text=format$(rs("importoordine"), "#0.00")
loop until rs.eof
rs.close
etc etc
ma non è più pratico assai???
(ho scritto a braccio...sigh scrivere ancora in vb6!!!!)
 Saluti...

 (ho scritto a braccio...sigh scrivere ancora in vb6!!!!)
Si infatti forse mi ero spiegato male.
Di norma 99.99% uso sempre Rs.Fields("pippo").Value, anche quando devo
ciclare non uso Rs.Fields(i).Value, ma un For Each.
Un esempio è questo:
(routine che da una stringa SQL mi fa insert into delle righe in una
tabella "speculare")
| Dim Campi As String, Valori As String, Campo as ADODB.Field 
| Do Until Rs.EOF
|     Campi = "":   Valori = ""
|     For Each Campo In Rs.Fields
|         Campi = Campi & Chr$(34) & Campo.Name & Chr$(34) & ","
|         Valori = Valori & TypeCampo(Campo) & ","
|     Next
|     If Len(Campi) > 0 Then Campi = Left$(Campi, Len(Campi) - 1)
|     If Len(Valori) > 0 Then Valori = Left$(Valori, Len(Valori) - 1)
| 
|     mySQL = "INSERT INTO " & NomeTab & " (" & Campi & ") VALUES (" & Valori & ");"
|     AdoCN.Execute mySQL
| 
|     Rs.MoveNext
| Loop



grazie della spiegazione.
molto utile
simona

 campi....quindi NON SO a prori come si chiama il mio field!
Perchè non può rispondere così?
Classico esempio in cui non sai i campi a priori e un ciclo di quel
tipo si applica: il programma ha una qualche funzionalità che permette
all'utente ricerche parametrizzate per cui di volta in volta non sai
quali campi e quali tabelle sono state generate nel tuo recordset.
Lo puoi invece recuperare a posteriori proprio analizzando ogni
singolo field dell'insieme, da cui il ciclo... questo vale tanto in
VB6 che in .NET