Newsgroup: it.comp.lang.delphi
Titolo: StoredProc e parametri

Salve,
ho definito nel DB (firebird) un bel po' di procedure.
Ora vorrei fare una procedura in Delphi (uso D2010) che abbia come 
parametri il nome della procedura DB e l'appropriata lista di parametri 
ed esegua la procedura DB.
Diciamo che per procedure DB che fanno inserimento o aggiornamento dei 
dati definisco una procedura delphi tipo:
procedura ExecProcedure(aProcName:string; ParamList:???);
Quando creo la StoredProc e assegno StoredProc.Connection i parametri 
vengono creati automaticamente. Devo quindi passare solo i valori.
Mi suggerite come fare? Mi sono perso fra puntatori, varianti e factory 
di StoredProc...
Enrico

 procedura ExecProcedure(aProcName:string; ParamList:???);
Ti garantisco che non e' per niente una buona idea.
A.

W dniu 2010-12-20 17:53, Alberto Salvati pisze:

Perche` no?
Ho qualche decina di stored procedure definite nel db.
Sarebbe interessante avere qualcosa tipo factory a cui passare i valori 
dei parametri e che chiami la procedura nel db.
Altrimenti per ogni procedura nel db devo fare una procedura che crea un 
TStoredProc, inserire i valori dei parametri e chiamare la procedura nel db.
Siccome le procedure differiscono solo per il nome della procedura del 
db da chiamare e per i parametri (che sono creati automaticamente) mi 
sembra che valga la pena provarci.
Cosa suggerisci come alternativa?
Enrico

> Perche` no?
Da quasi 2 anni sto in pratica facendo re factoring su uno dei moduli
di un applicativo.
E questo modulo pullula di varie trovate geniali, compresa questa.
Dal mio punto di vista si tratta di un pessimo esempio di
generalizzazione.
Un conto e' avere un oggetto applicativo che espone dei metodi insert/
delete/update...
In tal caso l'oggetto applicativo "sa" quale e' la stored procedure
che gli serve e sa come usarla.
Ma avere un posto centralizzato da dove passa tutto e' una cosa che
non condivido.
Ad esempio, ho delle stored procedure che differiscono leggermente per
il modo di funzionamento...
Alcune di esse hanno dei parametri di tipo in/out, ma attenzione, solo
*alcune* di esse.
Quindi, i parametri alla procedure delphi dovrebbero essere passati
come var e non come const...
E poi, come fai a stabilire quali sono i parametri che eventualmente
devono essere restituiti modificati e quali no?
Dovresti iniziare a mettere degli if, giusto?
Magari, poi, oggi le stored proc sono tutte simili ma poi tra n giorni
scopri che te ne serve una fatta in modo diverso...
E ti toccherebbe o diversificare, chiamandola direttamente oppure
modificare la proc delphi da cui passano tutti.
Quindi, la domanda diventa:
vale la pena mettersi una "bomba a orologeria" come questa sotto il
sedere per i dubbi vantaggi che forse potrebbe portare?
Io a questa domanda rispondo sempre "no".
Ho imparato a mie spese che se il prezzo da pagare per risparmiare
qualche riga e' ridurre la linearita', la semplicita' e la
possibilita' di modificare in tempi brevissimi il comportamento di un
qualcosa, il gioco non vale la candela.
Ribadisco che IO credo sia una pessima idea...
Questo non vuol dire ovviamente (ma non per tutti) che tu debba
prendere per oro colato quello che dico.
Io mi limito a darti un suggerimento: poi vedi tu! :-D
L'alternativa dipende da come stai lavorando.
A.


La mia idea era per una serie di procedure di inserimento di dati che 
invocano procedure definite nel db.
Volevo centralizzare il processo di creazione della TStoredProc, la sua 
connessione al db, l'esecuzione e la distruzione.
Al momento della connessione al db la TStoredProc ottiene i parametri 
che deve passare al db.
Si tratta quindi "solo" di inizializzare i parametri. Non penso sia 
causa di grossi problemi. Ho esaminato il codice delle procedure nel db 
e sono tutte simili.
La centralizzazione evita la ripetizione di codice in decine di 
procedure. Tale ripetizione nel caso di modifica puo` a sua volta essere 
causa di errori e da considerarsi un potenziale bomba.
Ovvaimente sono due filosofie di programmazione diverse (con pregi e 
difetti) e la scelta e` personale...
Quindi a questo punto generalizzando il problema la domanda diventa:
Come passare a una procedura una serie di valori di diversi tipi?
Ho pensato ai Variant (che non conosco troppo bene) anche perche` i 
TParam.Value dei parametri della TStoredProc sono di tipo Variant.
E qui non so andare avanti... come passo un immagine a un variant e come 
assegno poi il contenuto di questo variant al variant TParam.Value in 
modo da salvare l'immagine in un blob?
Enrico

 Cosa suggerisci come alternativa?
La soluzione ideale per te sarebbe di dotarti di un OPF/ORM [2], ma 
credo che tu non lo stia usando e sarebbe complesso da introdurre in 
corso d'opera.
Non ti resta che ponderare attentamente l'idea di crearti una factory e, 
alla fine, fare quello che si fa pių spesso: provare, scrivere codice, 
accorgersi di sbagliare e migliorarsi, oppure felicitarsi che hai fatto 
la scelta corretta.
Ovviamente parti dall'assunto che non esiste "LA" soluzione: in 
informatica le cose si possono fare in tantissimi modi differenti.
Rif.
[1] 
http://en.wikipedia.org/wiki/Create,_read,_update_and_delete
[2] 
http://stackoverflow.com/questions/422426/orm-for-delphi-win32
-- 
morde
D5,D6,D7
QT 4.7.0 - MingW- QT Creator 2.1


Ottimi link
Grazie ancora
Enrico

 da assegnare.
E come fai a sapere come assegnare i valori ai variant?
Cioč, se non distingui la stored procedure e generalizzi, stai assumendo 
che per esempio il primo parametro sia sempre una stringa, il secondo 
sempre un numero, il terzo sempre una data, per tutte le stored procedure.
Oppure ho capito male io?
Il "case" serve proprio a distinguere il tipo di "cast" che dovrai 
operare sui variant in relazione all'ordine dei parametri dichiarati 
nella specifica stored procedure.



Il TStoredProc ha la lista dei parametri definita dal collegamento al db.
La funzione chiamante deve passare i parametri nell'ordine giusto.
A questo punto non sono sicuro di come funzionino i variant.
La cosa migliore sarebbe assegnare/copiare la property Value dei variant 
passati alla procedura ExecStoredProc ai variant dei parametri del 
TStoredProc nell' ordine in cui sono definiti.
Funziona? Non ho ancora provato (sto lavorando sul db adesso)
Se funziona, siccome tstoredproc.Params[x].DataType e` gia` settato dal 
collegamento al db, TStoredProc sa come trattare il Params[x].Value
e non dovrebbe essere necessario il case.
Se non funziona pensavo di sfruttare ancora il DataType dei params e 
usare un case sui vari tipi di DataType.
Comunque fondamentale e` la "sincronizzazione" (ordine) fra i parametri 
passati alla procedura ExecStoredProc e i tstoredproc.Params a cui 
vengono associati i valori.
Se ho un attimino di tempo oggi ci provo e posto il risultato
Enrico


Ammesso che centralizzare sia la cosa giusta, se passi un variant alla 
procedura:
procedura ExecProcedure(aProcName:string; aParamList:variant);
puoi popolare il variant con la funzione VarArrayOf ed usarlo nel modo 
che si usa la funzione locate del dataset ottenendo un Array già 
ordinato e con un numero di elementi variabili.
Esempio:
ParamList: Variant;
ParamList := VarArrayOf([Par1, Par2, Par3,...]);
Dall'Help:
VarArrayOf returns a one-dimensional Variant array with the elements 
given by the Values parameter.
The low bound of the returned array is zero, the high bound is the 
number of values given by the Values parameter less one, and the element 
type is Variant.

 e non dovrebbe essere necessario il case.
Si, anche secondo me se passi il variant al .value dovrebbe 
autocastarselo da solo.
Facci sapere l'esito del test.
Ciao


 modo da salvare l'immagine in un blob?
vedi se ti sono d'aiuto questi articoli:
http://www.delphi3000.com/articles/article_640.asp
http://www.delphi3000.com/articles/article_641.asp
leggi bene nei commenti che ci sono dei chiarimenti sull'uso delle 
procedure.
ciao


 
http://www.delphi3000.com/articles/article_641.asp
Il primo lo avevo gia` trovato...
Comunque volevo qualcosa di piu` generico, cosi` ho usato un filer:
http://www.delphi3000.com/articles/article_2991.asp
Sono partito da queste procedure
procedure WritePictureToStream(Picture: TPicture; Stream: TStream);
procedure ReadPictureFromStream(Picture: TPicture; Stream: TStream);
Sfruttando i link inviati da morde ho modificato le procedure:
procedure PictureToVariant(const aPic:TPicture; var aVar:Variant);
procedure VariantToPicture(const aVar:Variant; aPic:TPicture);
in modo che usino le procedure del filer.
Adesso posso copiare un immagine (TPicture) qualsiasi in un variant e 
recuperla dal variant.
L'ultimo passo e` la definizione della procedura che inserisce i dati 
nel db utilizzando una stored procedure del db stesso:
function TdmServer.ExecStoredProc(const aProcName: string; aVarList: 
Variant): Integer;
var
   lT:TDBXTransaction;
   lsp:TSQLStoredProc;
   i: Integer;
begin
   lsp:=TSQLStoredProc.Create(nil);
   try
     lsp.SQLConnection:=GetDBConnection; // connects to the SQLConnection
     if not lsp.SQLConnection.Connected
       then lsp.SQLConnection.Connected := True;
     lsp.StoredProcName := aProcName;
     lsp.Active := False;
     for i := 0 to lsp.Params.Count - 1 do begin
       lsp.Params[i].Value:=aVarList[i];
     end;
     lT := lsp.SQLConnection.BeginTransaction;
     try
       lsp.ExecProc;
       lsp.SQLConnection.CommitFreeAndNil(lT);
     except
       lsp.SQLConnection.RollbackFreeAndNil(lT);
     end;
   finally
     lsp.Close;
     lsp.Free;
   end;
end;
Quindi l'inserimento di dati diventa:
procedure InsertMyData;
var
   vImg:Variant;
begin
PictureToVariant(MyImage.Picture, vImg);
ExecStoredProc('ADD_MY_DATA', VarArrayOf(['MyString', vImg, 1]));
end;
Da migliorare e` il ciclo for.
Penso sia meglio passare anche una lista di nomi dei Params e usare 
ParamByName piuttosto che Params[i].
Cosi` posso passare solo i parametri che mi interessano.
Ad esempio non devo passare un valore per il parametro che fa 
riferimento alla primary key e che viene inserito nella stored procedure 
del db prendendolo dal generator.
Grazie per i consigli
Buon Natale
Enrico

 Cosi` posso passare solo i parametri che mi interessano.
Ottimo lavoro :)
