Italian community of Lazarus and Free Pascal

Programmazione => Databases => Topic aperto da: petrusic - Agosto 25, 2022, 06:33:34 pm

Titolo: mie utility su database
Inserito da: petrusic - Agosto 25, 2022, 06:33:34 pm
Nel costruire il mio file di utility per accedere al database coi miei dati (SQLite3), ho inserito nel mio primo programma alcune funzioni  come
Codice: [Seleziona]
 function OpenDbQuery1F1(sql: String): Boolean;
 function OpenDbQuery2F1(sql: String): Boolean; 
Ciò, perchè mentre è ancora attiva la Query1F1, mi è successo di dovere eseguire una Query2F1. Bene

Sto ora scrivendo un altro programma che accede sempre allo stesso database, ma  soltanto attraverso la Query1F1, quindi non ha bisogno di un secondo oggetto Query.

Ebbene, la compilazione di quest'ultimo programma mi restituisce un mesaaggio d'errore perchè non trova la Query2F1.

Francamente non me l'aspettavo. Non capisco perchè, se il file di utility contiene due function  di tipo Query, ma al programma corrente occorre richiamarne una sola, il compilatore debba protestare.

Io non so come fate voi di fronte ad una situazione del genere.

Io attualmente vedo solo una via: non utilizzare file utility, ma scrivere le function direttamente dentro ciascun programma.
Mi sembra una scelta bambinesca, ma non saprei fare diversamente.

Per completezza riporto le immagini dei form dei due diversi programmi.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 25, 2022, 07:09:35 pm
Non hai importato nella Uses il corretto file delle definizioni.

Inoltre hai definito anonime le funzioni sotto elencate.

Se le vuoi anonime ma di uso globale, devi definirle nel "global section" (cioè nella sezione INTERFACE)

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 25, 2022, 10:08:22 pm
Non hai importato nella Uses il corretto file delle definizioni.

Inoltre hai definito anonime le funzioni sotto elencate.
Riporto qui le uses dei due programmi, perchè non ho capito dove avrei sbagliato.

1-Progetto "DomusRatioCancResta"
Codice: [Seleziona]
unit frm1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, DB, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ZConnection, ZDataset,
  utilmie, utilmiedb;

type

  { TForm1 }

  TForm1 = class(TForm)
    BPulsAvanti: TButton;
    Button2: TButton;
    DataSource1: TDataSource;
    Label1: TLabel;
    LBggContab: TListBox;
    ZConnection: TZConnection;
    ZQuery1: TZQuery;
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure LBggContabEnter(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

  dbCopia, dbCorr, dbOrig, dbProve, percorso: String;

implementation

{$R *.lfm}

{ TForm1 }
var
  totRec: Integer;

//  dataOggi,   dbCopia, dbCorr, dbOrig, dbProve, percorso, sql, status ,striMia: String;
  dataOggi, sql, status ,striMia: String;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Caption:= 'CANCELLAZIONE, in Tb.restacassagg di tutti i record di una giornata contabile';
  percorso := ExtractfilePath(Application.ExeName);
//------------------------------------ Assegno percorso per l'accesso al DB corretto
  dbCopia:= '/media/dirdati/dativari/contabfam/ContabFamdb(copia)';         // )
  dbOrig:= '/media/dirdati/dativari/contabfam/ContabFamdb';                 // ( per impostare dbCorr:= dbOrig o dbProve -> Form1.Panel1Enter
  dbProve:= '/media/dirdati/dativari/contabfam/ContabFamdb_prove';          // )
//------------------------------------------------------------------------------------
end;                                           

2-Progetto DomusRatio (Di questo riporto soltanto la parte iniziale della unit frm1
Codice: [Seleziona]
unit FrmMain;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, DB, Forms, Controls, Graphics, Dialogs, Menus, ExtCtrls,
  FileUtil, StdCtrls, ZConnection, ZDataset, Frm5, Frm6, Frm7,
  utilmie, utilmiedb;
//  FileUtil, process, StdCtrls, ZConnection, ZDataset, Frm5, Frm6, Frm7,



type

    { TForm1 }

    TForm1 = class(TForm)
    DataSource1: TDataSource;
    DataSource2: TDataSource;
    Image1: TImage;
    LAttesa: TLabel;
    MainMenu1: TMainMenu;
    Menu1: TMenuItem;
    Menu11: TMenuItem;
    Menu2: TMenuItem;
    Menu21: TMenuItem;
    Menu22: TMenuItem;
    Menu221: TMenuItem;
    Menu222: TMenuItem;
    Menu2221: TMenuItem;
    Menu2222: TMenuItem;
    Menu2223: TMenuItem;
    Menu2224: TMenuItem;
    Menu2225: TMenuItem;
    Menu223: TMenuItem;
    Menu224: TMenuItem;
    Panel1: TPanel;
    ZConnection: TZConnection;
    ZReadOnlyQuery1: TZReadOnlyQuery;
    ZReadOnlyQuery2: TZReadOnlyQuery;
    procedure FormCreate(Sender: TObject);
    procedure Menu11Click(Sender: TObject);
    procedure Menu21Click(Sender: TObject);     // Inserimento Movimenti giornalieri
    procedure Menu221Click(Sender: TObject);    // Ricerca Movimenti per Descrizione (tipoRicerca:= '0')
    procedure Menu2221Click(Sender: TObject);   // Ricerca Movimenti per Voce principale di Cassa (tipoRicerca:= '1')
    procedure Menu2222Click(Sender: TObject);   // Ricerca Movimenti per Voce di Sottoconto di Cassa (tipoRicerca:= '2')
    procedure Menu2223Click(Sender: TObject);   // Ricerca Movimenti per Voce Primaria di Collegamento (tipoRicerca:= '3')
    procedure Menu2224Click(Sender: TObject);   // Ricerca Movimenti per Voce Secondaria di Collegamento (tipoRicerca:= '4')
    procedure Menu2225Click(Sender: TObject);   // Ricerca Movimenti per Voce di Sottoconto di Collegamento (tipoRicerca:= '5')
    procedure Menu223Click(Sender: TObject);    // Ricerca per Componente familiare (tipoRicerca:= '6')
    procedure Menu224Click(Sender: TObject);    // Ricerca per Importo (tipoRicerca:= '7')
    procedure Panel1Enter(Sender: TObject);
end;

const
  maskLire: String = '#,###,##0';
  maskEuro: String = '#,###,##0.#0';

var
  Form1: TForm1;


  dbCopia: String;
  dbOrig: String;
  dbProve: String;
  dbCorr: String;
  dataCont, dataSys, nomeGiorno, nomeMese, percorso: String;
  ggApertaChiusa: String;      //  (campo vuoto) = Giornata contabile NUOVA,   "C" = Giornata contabile CHIUSA,   "A" = Giornata contabile APERTA
  tipoValuta: String;


implementation
{$R *.lfm}

{ TForm1 }

procedure TForm1.Menu11Click(Sender: TObject);   // Scelta Menu' "File + Esci"
begin
  Application.Terminate;
end;                                     

I file delle mie utility sono dentro un'unica directory:
Codice: [Seleziona]
$ ls /media/dirdati/dativari/lazarus_progetti/lazarus_progetti_miei/util_mie
backup  daticomuni.pas  lib  utilmiedb.pas  utilmie.pas
riporto parte del contenuto del file "utilmiedb.pas"
Codice: [Seleziona]
unit utilmiedb;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Dialogs,
//  DB, Forms, Controls, Graphics, Menus, ExtCtrls,
  DB, Forms, Controls, Menus, ExtCtrls,
  FileUtil, StdCtrls,
  ZConnection, ZDataset;


type
  TstrOut = Integer;

 function DbContaRecInOpen(sql: String): Integer;    // Esegue la OPen del DB tramit Query contenuta in "sql" ed accerta la presenza di record di riscontro
 function EstraiVocePianCont(sql: String): String;    // Estrae dalla Tabella "piancont" la Voce contabile, a cui accoda il contenuto del campo "ContrPartSiNo"
 function EstraiCoVoColleg(sql, segnal: String): String;    // Estrae dalla Tabella "racodvoci" il codice di sottoconto contrappoto al tipo inmdicato in "segnal"
 function OpenDbQuery1F1(sql: String): Boolean;
 function OpenDbQuery2F1(sql: String): Boolean;

implementation
uses
  frm1;
function OpenDbQuery1F1(sql: String): Boolean;     // funzione usata per accedere alla lettura di un solo Record della TB.piancont o dalla TB.racodvoci
var
  swOpenErro: Boolean = False;
begin
//----------- Open del DB ------------------------------------------------------
  Form1.ZConnection.Database := dbCorr;
  Form1.ZQuery1.SQL.Text := sql;
  try
    Form1.ZQuery1.Open;
    except
    on E: Exception do
    begin
      WriteLn('ERRORE nella OPEN del ContabFamdb (sql: "' + sql + '" ' + IntToStr(E.HelpContext) + ': ' + E.Message);
      swOpenErro:= True;
    end;
  end;
  Result:= swOpenErro;
end;
function OpenDbQuery2F1(sql: String): Boolean;     // funzione usata per accedere alla lettura di un solo Record della TB.piancont o dalla TB.racodvoci
var
  swOpenErro: Boolean = False;
begin
//----------- Open del DB ------------------------------------------------------
  Form1.ZConnection.Database := dbCorr;
  Form1.ZReadOnlyQuery2.SQL.Text := sql;
  try
    Form1.ZReadOnlyQuery2.Open;
    except
    on E: Exception do
    begin
      WriteLn('ERRORE nella OPEN del ContabFamdb (sql: "' + sql + '" ' + IntToStr(E.HelpContext) + ': ' + E.Message);
      swOpenErro:= True;
    end;
  end;
  Result:= swOpenErro;
end;                                                       

Spero che possa bastare per aiutarmi a capire come dovrei intervenire.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 25, 2022, 10:40:22 pm
Le dichiarazioni dovrebbero essere a posto. Non posti però la parte di codice dove usi la funzione (con le Uses).

Se nel codice dove usi la funzione non genera errore e genera solo l'errore per la "mancanza" dell'altra che però non usi, allora è un bel dilemma.

Io non saprei come aiutarti.

Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 26, 2022, 11:40:02 am
Le dichiarazioni dovrebbero essere a posto. Non posti però la parte di codice dove usi la funzione (con le Uses).
Se nel codice dove usi la funzione non genera errore e genera solo l'errore per la "mancanza" dell'altra che però non usi, allora è un bel dilemma.
Cerco di riportare il codice con ordine
Il programma richiamala la  funzione "DbContaRecInOpen" , presente nella unit "utilmiedb" delle utility mie:
Codice: [Seleziona]
unit frm1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, DB, Forms, Controls, Graphics, Dialogs, StdCtrls,
  FileUtil, ZConnection, ZDataset,
  utilmie, utilmiedb;

type

  { TForm1 }

  TForm1 = class(TForm)
    BPulsAvanti: TButton;
    Button2: TButton;
    DataSource1: TDataSource;
    Label1: TLabel;
    LBggContab: TListBox;
    ZConnection: TZConnection;
    ZQuery1: TZQuery;
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure LBggContabEnter(Sender: TObject);
  private

  public

  end;       
. . .
procedure TForm1.LBggContabEnter(Sender: TObject);
var
  dtGGfa: String;
begin
 BPulsAvanti.Caption:='Seleziona data per Cancellazione Record e' + LineEnding + Space(30) + 'CLICCA QUI';
 dataOggi:= CarDtCalendario();
 dtGGfa:= CalcDataGGfa(30);
  dbCorr:= dbProve;
  CopyFile(dbOrig, dbCorr);
  ShowMessage('----- DomusRatioCancResta -----' + System.lineending + 'DB Origine: ' + dbOrig + System.lineending + 'copiato in ' + System.lineending + 'DB di prova: ' + dbProve);
 //----------- Open del DB ------------------------------------------------------
  ZConnection.Database:= dbCorr;
  sql:= 'SELECT DtCoMovg, StaDtMovg FROM riepmovg WHERE DtCoMovg <= ' + dataOggi + ' AND DtCoMovg >= ' + dtGGfa + ' ORDER BY DtCoMovg DESC';
  totRec:= DbContaRecInOpen(sql);                                         

l'unit utilmiedb contiene la function DbContaRecInOpen:
Codice: [Seleziona]
unit utilmiedb;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Dialogs,
//  DB, Forms, Controls, Graphics, Menus, ExtCtrls,
  DB, Forms, Controls, Menus, ExtCtrls,
  FileUtil, StdCtrls,
  ZConnection, ZDataset;


type
  TstrOut = Integer;

 function DbContaRecInOpen(sql: String): Integer;    // Esegue la OPen del DB tramit Query contenuta in "sql" ed accerta la presenza di record di riscontro
 function EstraiVocePianCont(sql: String): String;    // Estrae dalla Tabella "piancont" la Voce contabile, a cui accoda il contenuto del campo "ContrPartSiNo"
 function EstraiCoVoColleg(sql, segnal: String): String;    // Estrae dalla Tabella "racodvoci" il codice di sottoconto contrappoto al tipo inmdicato in "segnal"
 function OpenDbQuery1F1(sql: String): Boolean;
 function OpenDbQuery2F1(sql: String): Boolean;

implementation
uses
  frm1;                                           


function DbContaRecInOpen(sql: String): Integer;    // Esegue la OPen del DB tramite Query contenuta in "sql" ed accerta la presenza di record di riscontro
var
  totRec: Integer;

begin
  Form1.ZConnection.Database:= dbCorr;
  Form1.ZQuery1.SQL.Text:= sql;
  try
    Form1.ZQuery1.Open;
    except
    on E: Exception do
      WriteLn('ERRORE nella OPEN-sql= "' + sql + ' -"' + IntToStr(E.HelpContext) + ': ' + E.Message + '"-');
  end;
  totRec:= Form1.ZQuery1.RecordCount;
  Result:= totRec;
  Form1.ZQuery1.Close;
end;                           
che è quella che viene richiamata.


Tuttavia nella unit sono presenti anche altre function, non richiamate nel programma corrente.
Ebbene il compilatore fornisce il seguente errore per ben 9 volte puntando a tutte le righe tipicamente anomale delle altre function, pur non essendo mai richiamate:
Citazione
Compila il progetto, Destinazione: DomusRatioCancResta: Codice di uscita 1, Errori: 9
utilmiedb.pas(54,9) Error: identifier idents no member "ZReadOnlyQuery2"
. . .
utilmiedb.pas(138,9) Error: identifier idents no member "ZReadOnlyQuery2"

Io vedo solo 2 alternative:
- creare una unit di utilility per ciascuna function:
- abbandonare il concetto unit di utility e riportare le function di volta in volta che servono, dentro il programma chiamante.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 26, 2022, 01:51:35 pm
Intanto devi riportare correttamente quello che accade: ciò che hai riportato adesso è ben diverso da quello di inizio topic, perchè altrimenti non riusciamo ad aiutarti.

Innanzitutto, la compilazione di una unità viene comunque eseguita, anche se l'unità stessa viene usata solo in parte.

In questo caso il compilatore di dice che la classe ZReadOnlyQuery2 "ha qualcosa che non và", questo indipendentemente dal fatto che venga usata o meno.

Se l'uso di quella classe non è corretto, ovviamente anche le funzioni / procedure / metodi che la usano non vengono compilati.

Non sò come e soprattutto dove viene dichiarata quella classe, ma è probabile che sia li il problema: qualche tua unità in uso chiama la FORM1 ?

Due cose però devo dirti sulla programmazione:

1) Quando crei una nuova unità, cerca per quanto possibile di non usare classi dichiarate "sotto" una FORM di un'altra unità. E' buona pratica non mantenere dipendenza da oggetti visuali dichiarate in altre unità, eccetto per chiamare metodi come costruttori, distruttori o Show e ShowModal.

2) Quando crei un unità con dipendenze, devi assolutamente verificare che tutte le dipendenze siano "istanziate", non basta solo includere una unita nelle USES perchè all'interno di quella unità qualcosa magari non verrà istanziato automaticamente.

Terza cosa, ancora come consiglio, le Query potresti istanziarle a runtime e definirle direttamente nella tua unità dove le usi senza usare il DRAG & DROP in una Form.

Ciao
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 26, 2022, 02:55:11 pm
Io vedo solo 2 alternative:
- creare una unit di utilility per ciascuna function:
- abbandonare il concetto unit di utility e riportare le function di volta in volta che servono, dentro il programma chiamante.

No, non funziona così. Il concetto di Unit è fondamentale nel Pascal e ha un senso ben preciso.

Nella programmazione ad oggetti il tutto si basa appunto sugli "oggetti" e ci sono alcune regole da seguire. Se si vuole programmare come nel vecchio "C" ci si scontra con delle difficoltà logiche.

In una Unit normalmente si raccolgono tutte le funzionalità logiche legate ad una specifica esigenza: se nella tua unit vuoi inserire delle funzioni che svolgano delle attività su un database, forse dovresti creare una classe di "utility" dove inserirai tutte le funzionalità che ti servono.
Tieni presente, visto che usi SQLite, che non puoi effettuare chiamate contemporanee (ad esempio da più Thread) al Database, tanto vale che definisci tutte le funzionalità che ti servono in quella Unit. Quindi "oggetti", "connessione", "query", etc ... al database saranno definiti li.

Ora viene il bello: il tutto sarebbe semplice se fosse fatto a runtime senza uso del "Drag & DROP" in una Form. Perchè se no torniamo punto a capo, creo un Form ci butto tutto dentro ... ma dipendo da un Form.
Lazarus (e non solo) però a pensato anche a questo e ha progettato una UNIT che serve proprio a questo scopo: si chiama "Modulo Dati" e deriva dalla classe TDataModule.

E' visuale e ci puoi mettere dentro i componenti del tuo DATABASE, ma è visuale SOLO a "design time" e serve appunto a facilitare la vita ai programmatori che possono usare il "Drag & Drop" per i componenti di gestione di un database ma non su una FORM. Non vanno inseriti i componenti visuali come "DBGRID" perchè come accennavo il TDataModule non è visuale a runtime.

Già questo potrebbe essere un buon punto di inizio per lavorarci.

Ahh, dimenticavo il "Modulo Dati" si crea andando nel menu "File", "Nuovo ..." e poi scegliendo "MODULO DATI".


EDIT: nel costruttore e nel distruttore del TDamodule puoi ad esempio creare la tua connessione al database e distruggerla alla fine del programma.

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 26, 2022, 03:05:48 pm
1) Quando crei una nuova unità, cerca per quanto possibile di non usare classi dichiarate "sotto" una FORM di un'altra unità. E' buona pratica non mantenere dipendenza da oggetti visuali dichiarate in altre unità, eccetto per chiamare metodi come costruttori, distruttori o Show e ShowModal.
Sono d'accordo, ma non saprei come regolarmi per accedere a tabelle di database. Ecco perchè ho utilizzato quel metodo.

Citazione da: DragoRosso
le Query potresti istanziarle a runtime e definirle direttamente nella tua unità dove le usi senza usare il DRAG & DROP in una Form.
Mi potresti fare un esempio pratico? Così non riesco a seguire il ragionamento.

Scusa, ma la terminologia inglese o inglesizzata non fa parte del mio linguaggio tecnico.



Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 26, 2022, 03:39:51 pm
Forse ho trovato:
http://www.lazaruspascal.it/index.php?page=111
Penso che è quello a cui ti riferisci quando parli di instanziare query a runtime.

Non mi sembra facile da comprendere, ma cerco di capire ugualmente e provo ad applicarlo praticamente.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 26, 2022, 04:36:47 pm
Forse quello è un pò troppo avanzato, usa le interfacce e cursori direttamente, ma se te la senti fai pure.

Ti posto intanto un esempio di runtime come lo pensavo io, forse più accessibile.

P.S.: è stato creato in Windows x64, c'è anche la dll di SQLite3. Adattalo eventualmente se usi Linux (in realtà non dovrebbero esserci modifiche se non nelle proprietà di compilazione).

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 26, 2022, 07:49:23 pm
Ti posto intanto un esempio di runtime come lo pensavo io, forse più accessibile.
P.S.: è stato creato in Windows x64, c'è anche la dll di SQLite3. Adattalo eventualmente se usi Linux (in realtà non dovrebbero esserci modifiche se non nelle proprietà di compilazione).

Ti ringrazio per la disponibilità e la pazienza. In effetti io cercavo qualcosa del genere sin da quando ho cominciato a provare l'accesso a database in Lazarus.

Ora che ho preso confidenza con gli strumenti grafici di Zeos, ricominciare praticamente da zero non è semplice, ma sono sicuro che ne vale la pena.

Ho dato una guardata veloce al progettino d'esempio che mi hai gentilmente girato. Ho visto che il programma principale richiama una unity al cui interno ci sono tre utility, di cui la prima è un costruttore, la seconda è un distruttore e l'ultima è la function vera e propria.

Nel costruttore  ho incontrato:
Codice: [Seleziona]
 ZConnection1 := TZConnection.Create(nil);    
. . .
ZTable1 := tZTable.Create(nil);   
Non ho capito se quel (nil) è un'impostazione obbligata o se, invece, può essere sostituita da nomi di tabelle(nella prima create) o da nomi di colonne (nella seconda create), come si potrebbe fare, per esempio in fase di generazione di un nuovo database, con le relative tabelle.

Poi credo che l'utilizzo segua il criterio di Zeoslib, cioè, se esiste una connessione già attiva ed occorre procedere con una seconda query, senza chiudere quella attiva, occorre dare vita ad una ZConnection2 dentro un'ulteriore unit, con un suo costruttore, un suo distruttore e la sua funzione di lettura, scrittura, aggiornamento, . . .  del database.

Spero di avere capito bene.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 26, 2022, 10:21:47 pm
Il parametro del costruttore Create generalmente viene usato per consentire l'apparentamento, ossia principalmente chi ha la responsabilità della distruzione del componente.

Nel caso sia nil, qualcuno dovrà chiamare il metodo free del componente.

In questo caso i due componenti che hai indicato vengono messi a FREE dal metodo DESTRUCTOR della classe.

Ora se osservi bene, vedrai che il codice nella parte visuale (unit1) costruisce la classe con il parametro a SELF, questo fà si non sia necessario chiamare il distruttore FREE manualmente in quanto vi provvede automaticamente la FORM1 (self in questo caso indica la FORM1).

Codice: [Seleziona]
MiaUtilita := TMyUtility.Create(self);

Così facendo il metodo DESTRUCTOR dell'istanza "MiaUtilita" verrà chiamato quando la FORM1 si chiude.

Per quello che riguarda la connessione, dentro la classe puoi fare quello che vuoi, inserire 10 ZQuery, 15 ZTable, 5 ZConnection, etc .... (sono numeri a caso chiaramente), e gestire ogni singolo componente o sua proprietà come meglio credi. Sei in una classe e tutti gli "oggetti" della classe appartengono alla classe e li dentro puoi fare ciò che vuoi.

Tra l'altro vedrai che ZConnetcion1 e ZTable1 sono dichiarate "public" e quindi puoi accedervi anche dalla Unit1. In realtà dovrebbero essere dichiarate "private" perchè dovresti accedervi solo con i metodi della classe "TMyUtility".

Codice: [Seleziona]
type
  TMyUtility = Class
  private
      ZConnection1: TZConnection;
      ZTable1: TZTable;
  public
      function LeggiLaPrimaRiga: boolean;
      constructor Create(Owner: TComponent); overload;
      destructor Destroy;                    override;
  end;     

Se vuoi continuare con le componenti grafiche, invece di usare un FORM usa un TDATAMODULE come ti ho indicato in un post precedente.

Magari poi "mixare" intanto le due tecniche finchè non prendi mano.

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 27, 2022, 04:20:18 pm
@ DragoRosso

Mentre ti comunico che, per terminare al più presto il programmino che avevo in corso d'opera, ho preferito mettere un pò da parte la novità che mi hai indicato per l'accesso ai DB,

Stamani ho finito di provarlo. Funziona benissimo.

Ora, però, visto che ho la calma necessaria per riconsiderare e modificare quanto fatto sui DB, mi pare il momento giusto per addentrarmi nella tecnica d'accesso RunTime.

Ho perciò ripreso l'esempio che mi hai postato ed ho cercato di applicarne il metodo nel programma appena finito che è il più semplice fra quelli realizzati.

Ho quindi cominciato a formare una Unit Class dove ho riportato il costruttoire ed il distruttore di una ZConection che, penso,  dovrebbe rendersi disponibile alle varie occorrenze. Andrebbe perciò personalizzata, al momento, nel costruttore.
Riprendo perciò le righe dell'esempio:
Codice: [Seleziona]
 with ZConnection1 do
    begin
      ControlsCodePage := cCP_UTF8;
      AutoEncodeStrings := True;
      ClientCodepage := 'UTF-8';
      Properties.Add('AutoEncodeStrings=True');
      Properties.Add('controls_cp=CP_UTF8');
      Properties.Add('codepage=UTF-8');
      Port := 0;
      Database := '.\db.s3db';          <== qui dovrei mettere il percorso completo del mio DB (dbProve, dbOrig)
      Protocol := 'sqlite-3';
    end;
Dovrei potere impostare la proprietà Database col percorso del DB da manipolare al momento:
- (dbOrig:= '/media/dirdati/dativari/contabfam/ContabFamdb';)
- (dbProve:= '/media/dirdati/dativari/contabfam/ContabFamdb_prove').

Ecco, non ho capito come intervenire, perchè è il programma chiamante che conosce il percorso corretto, Ma come faccio a passargli la stringa? Qui?
Codice: [Seleziona]
MiaUtilita := TMyUtility.Create(self, dbOrig)
   

Lo stesso problema nasce quando, subito dopo dovrei impostare i parametri di identificazione della tabella subordinata del DB:
Codice: [Seleziona]
 ZConnection1.Connected:= True;
  ZTable1 := tZTable.Create(nil);
  with ZTable1 do
    begin
      Connection := ZConnection1;
      SortedFields := 'ID';
      TableName := 'utenti';
      IndexFieldNames := 'ID Asc';
    end;
perchè può NON essere sempre  la stessa.

Mi dispiace chiedere, ma purtroppo non riesco ad andare avanti da solo.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 27, 2022, 07:31:57 pm
Nel costruttore della tua classe (TMyUtility) puoi fare solo qualcosa, ad esempio solo istanziare (ossia creare) la connessione senza definire il database ne attivare la connessione stessa. Idem per la ZTable.

Poi puoi definire una funzione dove vai a passare i parametri che preferisci per impostare il database e la connessione.

Ecco, non ho capito come intervenire, perchè è il programma chiamante che conosce il percorso corretto, Ma come faccio a passargli la stringa? Qui?
Codice: [Seleziona]
MiaUtilita := TMyUtility.Create(self, dbOrig)
   

Come ti accennavo puoi creare nuove funzioni in cui passi ciò che vuoi, espandere come hai indicato il costruttore, etc ...

La progettazione di una classe dipende da cosa devi fare e può richiedere anche giorni o settimane per arrivare ad una versione sufficientemente funzionale.

Devi avere ben chiaro:

1) Scopo della classe: esempio generalizza e isola dal codice l'accesso al database... pensa se domani usassi MySql oppure usi qualcosa di diverso da ZEOS, dovresti solo cambiare o meglio implementare la classe e ricompilare senza cambiare una virgola al tua programma.

2) Componenti della classe (chiamati membri): devi prima di tutto definire quali componenti devi avere nella classe (ad esempio quante ZConnection ? quante ZQuery ?). Ovviamente ci dovranno essere anche tutte le variabili di supporto e di stato.

3) Anche se in un primo momento puoi esporre i componenti come "public", magari per facilitare l'accesso al codice vecchia maniera, poi dovrai pian piano definire le proprietà che ti consentano di interagire con le tue Query e le tue Tabelle, proprietà delle classe senza accedere direttamente ai membri interni.

4) Lo stato della classe deve essere sempre congruo, quindi dovrai provvedere a fare si che se usi il multithreading ci siano delle barriere di protezione (per ora è meglio che questo punto lo "appunti" e lo dimentichi).

Hai già sviluppato un applicativo, creare una classe funzionale è il prossimo esercizio, con tanto di procedure funzioni e proprietà.

Giusto per farti capire (il codice è indicativo e non completo):

Codice: [Seleziona]
type
  TMyUtility = Class
  private
      ZConnection1: TZConnection;
      ZTable1: TZTable;
  public
      property ElencoDatabase: TStringList read getElencoDatabase write setElencoDatabase;
      function LeggiLaPrimaRiga: boolean;
      constructor Create(Owner: TComponent); overload;
      destructor Destroy;                    override;
  end;     

C'è una nuova proprietà (che deve essere implementata con una funzione e una procedura "private" ovviamente) che definisce ad esempio il passaggio sia in scrittura che in lettura di quel famoso elenco di nomi dei database. Come vedi è una proprietà e chi la chiama non sà cosa c'è dopo, sà solo che puo passare un elenco di stringhe o ricevere un elenco di stringhe. Sarà la classe a definire come usare questo elenco (sia in ingresso che in uscita).

Per ora basta così ... alla prossima ti chiuderò la proprietà come esempio.

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 27, 2022, 11:11:12 pm
2) Componenti della classe (chiamati membri): devi prima di tutto definire quali componenti devi avere nella classe (ad esempio quante ZConnection ? quante ZQuery ?). Ovviamente ci dovranno essere anche tutte le variabili di supporto e di stato.
Allo stato attuale, tali componenti non mi sono noti, perchè inizialmente sono limitati, ma potranno crescere con le conoscenze e con l'uso che ne deriva dalle necessità legate ai prossimi programmi.

quote author=DragoRosso]
C'è una nuova proprietà (che deve essere implementata con una funzione e una procedura "private" ovviamente) che definisce ad esempio il passaggio sia in scrittura che in lettura di quel famoso elenco di nomi dei database. Come vedi è una proprietà e chi la chiama non sà cosa c'è dopo, sà solo che puo passare un elenco di stringhe o ricevere un elenco di stringhe. Sarà la classe a definire come usare questo elenco (sia in ingresso che in uscita).
Codice: [Seleziona]
      property ElencoDatabase: TStringList read getElencoDatabase write setElencoDatabase;
[/quote]
Per ora non capisco quale sia la sua utilità, perchè m'immagino che una lettura o scrittura su una tabella di DB (sia fisica che logica) si faccia sempre attraverso il trasferimento di una stringa, in formato sql, nella proprieta ZQuery1.SQL.Text, anche se fino ad ora non ho incontrato niente del genere.

Mi pare che tu non mi stia dando un aiuto, ma tutt'altro.
Non voglio tediarti ulteriormente. Ho capito che  Il percorso è lungo ed impegnativo e non mi pare corretto approfittare così tanto della tua disponibilità.

Grazie!

Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 28, 2022, 12:07:38 pm
Codice: [Seleziona]
      property ElencoDatabase: TStringList read getElencoDatabase write setElencoDatabase;
Per ora non capisco quale sia la sua utilità, perchè m'immagino che una lettura o scrittura su una tabella di DB (sia fisica che logica) si faccia sempre attraverso il trasferimento di una stringa, in formato sql, nella proprieta ZQuery1.SQL.Text, anche se fino ad ora non ho incontrato niente del genere.

Mi pare che tu non mi stia dando un aiuto, ma tutt'altro.
Non voglio tediarti ulteriormente. Ho capito che  Il percorso è lungo ed impegnativo e non mi pare corretto approfittare così tanto della tua disponibilità.
Grazie!
L'aiuto che ti posso dare è spiegarti alcune tecniche di programmazione, non posso certo sostituirmi a te.

Quella proprietà era solo un esempio. E' opportuno comunque che tu prosegua quando avrai qualcosa di concreto.

Ciò che ti ho spiegato è una delle innumerevoli strade che si possono percorrere, e ognuno deve trovare la propria.

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 28, 2022, 05:50:52 pm
Ciò che ti ho spiegato è una delle innumerevoli strade che si possono percorrere, e ognuno deve trovare la propria.
Quella che conosco io non riguarda l'accesso al DB da codice. Se ci fosse una guida dettagliata sull'argomento, mi permetterebbe di accedervi in qualsiasi momento senza farmi sentire a disagio per il continuo chiedere.

Citazione da: DragoRosso
E' opportuno comunque che tu prosegua quando avrai qualcosa di concreto
Vuoi dire che ci vorrebbe un programma di un certo spessore di impegno ed utilizzo?
Ma quello di contabilità familiare che sto riscrivendo in Lazarus Free Pascal andrebbe benissimo, ma  . . .   :-[

Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 28, 2022, 11:07:25 pm
@ DragoRosso
in questo post (http://www.lazaruspascal.it/index.php?topic=2661.msg17493#msg17493) ho trovato uno spunto di partenza per accedere al mio databas SQLite3.
Dopo avere letto una vecchia guida borland (http://www.thebat.altervista.org/sw/Manuali/Borland%20-%20Guida%20alla%20programmazione%20di%20Delphi%207.pdf), ho creasto un modulo dati.
Come prima operazione vorrei eseguire la Open del database, però quella guida non spende granchè sul da farsi relativamente ai database. Non ho trovato altro.
Comunque, con la creazione del suddetto modulo, è stata aggiunta una nuova unit (unit1):
Codice: [Seleziona]
unit Unit1;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TDataModule1 }

  TDataModule1 = class(TDataModule)
    procedure DataModuleCreate(Sender: TObject);
  private

  public

  end;

var
  DataModule1: TDataModule1;

implementation

{$R *.lfm}

{ TDataModule1 }

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin

end;

end.
Ora dovrei scriverci dentro le istruzioni per aprire il database, sfruttando gli strumenti Zeoslib.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 29, 2022, 10:25:04 am
Ora dovrei scriverci dentro le istruzioni per aprire il database, sfruttando gli strumenti Zeoslib.

Codice: [Seleziona]
unit Unit1;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, ZCompatibility,
  ZConnection, ZDataset, SQLite3Conn, SQLDB, DB;

type

  { TDataModule1 }

  TDataModule1 = class(TDataModule)
    procedure DataModuleCreate(Sender: TObject);
  private
    ZConnection1: TZConnection;
   
  public

  end;

var
  DataModule1: TDataModule1;

implementation

{$R *.lfm}

{ TDataModule1 }

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnection1 := TZConnection.Create(nil);
  with ZConnection1 do
    begin
      ControlsCodePage := cCP_UTF8;
      AutoEncodeStrings := True;
      ClientCodepage := 'UTF-8';
      Properties.Add('AutoEncodeStrings=True');
      Properties.Add('controls_cp=CP_UTF8');
      Properties.Add('codepage=UTF-8');
      Port := 0;
      Database := '.\db.s3db';
      Protocol := 'sqlite-3';
    end;
  ZConnection1.Connected:= True;

end;

end.
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 29, 2022, 04:50:46 pm
@ DragoRosso
Grazie per l'instradamento.

Leggendo il codice che hai preparato per me, trovo:

Codice: [Seleziona]
unit Unit1;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, ZCompatibility,           // ZCompatibility per la prima volta
  ZConnection, ZDataset, SQLite3Conn, SQLDB, DB;         // SQLite3Conn, SQLDB per la prima volta
Per capire, comincio da qui, perchè alcune classi richiamate in uses non le mai incontrate prima d'ora, usando i componenti grafici di Zeoslib (ZConnection, Zquery, DataSource). Ritengo che siano necessari nella gestione di DB, tramite codice.

La procedura
Codice: [Seleziona]
procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnection1 := TZConnection.Create(nil);
  with ZConnection1 do
    begin
      ControlsCodePage := cCP_UTF8;     //---  come prima                                <---------|
      AutoEncodeStrings := True;      //---  come prima                             <------|       |
      ClientCodepage := 'UTF-8';      //--- prima non me ne preopccupavo                   |       |    <-----|
      Properties.Add('AutoEncodeStrings=True');     //--- sembra la ripetizione  di      --|       |          |
      Properties.Add('controls_cp=CP_UTF8');                 //--- sembra la ripetizione  di     --|          |
      Properties.Add('codepage=UTF-8');                                     //--- sembra la ripetizione  di --|
      Port := 0;                                            //---  come prima 
      Database := FrmMain.dbCorr;        //--- caricando così il percorso corretto, col DB effettivo o quello di prova
      Protocol := 'sqlite-3';                               //---  come prima 
    end;
  ZConnection1.Connected:= True;
end;
è il costruttore per la connessione al DB?

Bene, la procedura appena creata nel 1° modulo dati permette di eseguire la Open del database, ma nel momento in cui dovessi andare a leggere o scrivere, occorre creare un altro modulo dati? e così per ogni nuova operazione sul DB?

Come vedi, ogni passo, ho sempre nuove domande da fare.  :-\ :-[
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 29, 2022, 05:51:39 pm
Per capire, comincio da qui, perchè alcune classi richiamate in uses non le mai incontrate prima d'ora, usando i componenti grafici di Zeoslib (ZConnection, Zquery, DataSource). Ritengo che siano necessari nella gestione di DB, tramite codice.

Puoi cancellare uno alla volta le unità che sembrano di troppo. Se il progetto compila vuole dire che erano effettivamente di troppo.

Codice: [Seleziona]
procedure TDataModule1.DataModuleCreate(Sender: TObject);
 ......
  Database := '.\db.s3db';          // se non lo imposto ora, ma soltanto quando eseguo la TForm1.FormCreate ? ......
end;

Tecnicamente non lo puoi fare nel TForm1.FormCreate, perchè l'istanza relativa alla TDataModule non è ancora stata "creata". Occhio alla catena di creazione degli oggetti.

Bene, la procedura appena creata nel 1° modulo dati permette di eseguire la Open del database, ma nel momento in cui dovessi andare a leggere o scrivere, occorre creare un altro modulo dati? e così per ogni nuova operazione sul DB?
Come vedi, ogni passo, ho sempre nuove domande da fare.  :-\ :-[

NEL TDATAMODULE, così come in una qualsiasi classe che costruisci, puoi metterci quanti componenti vuoi.

Ti allego un progetto db demo che contiene un TDataModule con i componentidi ZEOS. Questo ti spiega come usare un TDataModule ... è come usavi tu la FORM con i componenti ZEOS.

Ora se vuoi partire a costruire a RUNTIME, incomincia a eliminare i singoli componenti UNO ALLA VOLTA e a sostituirne il codice. Per la ZConnection sai come fare ... inizia da li.

P.S: SE FRA LE USES ci sono unità che danno errore (come ad esempio odbc...) semplicemente eliminale.

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 29, 2022, 06:13:49 pm
Ti allego un progetto db demo che contiene un TDataModule con i componentidi ZEOS. Questo ti spiega come usare un TDataModule ... è come usavi tu la FORM con i componenti ZEOS.

Ora se vuoi partire a costruire a RUNTIME, incomincia a eliminare i singoli componenti UNO ALLA VOLTA e a sostituirne il codice. Per la ZConnection sai come fare ... inizia da li.
Va bene. Grazie!
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 29, 2022, 09:36:23 pm
@ DragoRosso
Ho guardato la demo, cercando di capire come è stato usato il modulo dati.

Intanto non ho trovato più il costruttore che m'aspettavo dopo l'esercizio precedente.
Ho visto poi che nella finestra del Modulo dati hai inserito tutti gli oggetti TZConnection, TZQuery e DataSource che avevo capito non andavano più impiegati nella tecnica digestione DB a runtime.

Sono rimasto abbastanza sorpreso, perchè così gli strumenti grafici ci  sono sempre, sono inseriti solo una volta, è vero, non sono dentro una classe di tipo Form e sono accessibili, tramite uses, da tutte le Form del progetto.

Provo a modificare il mio programma e ti faccio sapere.

Ho notato però che la
Codice: [Seleziona]
procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnection1.Connected:= True;
  ZTable1.Active:= True;
  ZReadOnlyQuery1.Active:= true;
end;
imposta a True sia la TZConnection che le TZQuery. Ma così, se non ho capito male, il database è aperto sin dal momento del create del Modulo-Dati, con le Query già pronte a fornire i dati.
E a proposito, nella modifica che ho cominciato, ho visto la il modulo-dati  veniva creato automaticamente insieme alla Form1. Io vorrei avviarne il create, al momento del bisogno, dentro la Form1, in modo da potere assegnargli il percorso corretto del database da collegare all'oggetto TZConnection.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 29, 2022, 11:35:38 pm
Togli le tre righe da DataModuleCreate e inserscile in una nuova procedura che chiamerai quando ne hai bisogno.

La procedura la creerai nel TDataModule sotto "public".

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 30, 2022, 04:18:12 pm
Togli le tre righe da DataModuleCreate e inserscile in una nuova procedura che chiamerai quando ne hai bisogno.
La procedura la creerai nel TDataModule sotto "public".

Non conoscendo l'effetto della creazione automatica del modulo-dati, all'avvio del progetto, vorrei capire fino a che punto mi conviene gestirlo manualmente, ma ciò dipende da cosa succede dopo ila sua creazione: Viene eseguita la Open del DB? Viene solo preparato l'ambiente operativo per agire sul DB, come riconoscimenta del DataSorce, attivazione della connessione, individuazione del percorso d'accesso al DB?

Fino ad ora, a parte avere disegnato gli oggetti Zeos nella scheda del modulo-Dati, ho soltanto personalizzato il  relativo file.pas:
Codice: [Seleziona]
unit UModDBcontfam;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, ZCompatibility,
//  ZConnection, ZDataset, DB;
  ZConnection, ZDataset, SQLite3Conn, SQLDB, DB;


type

  { TDataModule1 }

  TDataModule1 = class(TDataModule)
  DataSourceX1: TDataSource;
  ZConnectionX1: TZConnection;
  ZQueryX1: TZQuery;
  ZReadOnlyQueryX1: TZReadOnlyQuery;

  procedure DataModuleCreate(Sender: TObject);
 
 

private


public

end;

var
  DataModule1: TDataModule1;

implementation

{$R *.lfm}

{ TDataModule1 }
uses
  FrmMain;

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnectionX1:= TZConnection.Create(nil);
  ZConnectionX1.Database := FrmMain.dbCorr;      // dbCorr contiene il percorso del file di DB corrente (Es.: dbProve:= '/media/dirdati/dativari/contabfam/ContabFamdb_prove'
end;
end.

Quindi ho lasciato la creazione automatica del Modulo-Dati.
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 30, 2022, 05:10:17 pm
Ho provato a modificare il mio sorgente considerando ora l'accesso al DB, ma, con l'inserimento del modulo-dati, cambia la mentalità di lettura e scrittura dati, perchè, pur modificando le vecchie istruzioni, ho ricevuo un messaggio d'Errore incomprensibile, per me.
Ecco il codice modificato:
Codice: [Seleziona]
sql:= 'SELECT IdRiepMovg FROM riepmovg';
  DataModule1.ZReadOnlyQueryX1.SQL.Text := sql;
  DataModule1.ZReadOnlyQueryX1.Active:= True;
  totRecQry:= DataModule1.ZReadOnlyQueryX1.RecordCount;           


L'Errore è:
Citazione
Il progetto DomusRatio ha sollevato una eccezione di classe 'EZDatabaseError' con messaggio:
Database connection component is not assigned.

 Nel file 'ZAbstractRODataset.pas' alla riga 2536

La riga uses del modulo dati contiene:
Codice: [Seleziona]
unit UModDBcontfam;

{$mode ObjFPC}{$H+}

interface

uses                                             
Classes, SysUtils, ZCompatibility,
  ZConnection, ZDataset, SQLite3Conn, SQLDB, DB;                 
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 30, 2022, 05:18:49 pm
E' probabile che la proprietà "Connection" del componente ZReadOnlyQueryX1 non è assegnata a una connessione (ZConnection).

Ciao
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 30, 2022, 10:01:42 pm
E' probabile che la proprietà "Connection" del componente ZReadOnlyQueryX1 non è assegnata a una connessione (ZConnection).
E' vero, mi ero dimenticato ad impostarla.

Però, purtroppo le anomalie non sono finite, perchè quando esegue
Codice: [Seleziona]
sql:= 'SELECT IdRiepMovg FROM riepmovg';
  DataModule1.ZReadOnlyQueryX1.SQL.Text := sql;

  DataModule1.ZReadOnlyQueryX1.Active:= True;           <--- questa istruzione

  totRecQry:= DataModule1.ZReadOnlyQueryX1.RecordCount;                           
viene generato un errore logico sulla sql
Citazione
SQL Error: SQL logic error.

Press OK to ignore and risk data corruption.
Press Abort to kill the program.

Ma la sql precedentemente non ha mai dato problemi.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Agosto 31, 2022, 11:12:06 am
Codice: [Seleziona]
sql:= 'SELECT IdRiepMovg FROM riepmovg';
  DataModule1.ZReadOnlyQueryX1.SQL.Text := sql;

  DataModule1.ZReadOnlyQueryX1.Active:= True;           <--- questa istruzione

  totRecQry:= DataModule1.ZReadOnlyQueryX1.RecordCount;                           
viene generato un errore logico sulla sql
Citazione
SQL Error: SQL logic error.

Press OK to ignore and risk data corruption.
Press Abort to kill the program.

Ma la sql precedentemente non ha mai dato problemi.

Il nome del campo "IdRiepMovg" o il nome della tabella "riepmovg" non è corretta (database errato ?). Questo è il motivo dell'errore.
Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 31, 2022, 04:15:50 pm
Il nome del campo "IdRiepMovg" o il nome della tabella "riepmovg" non è corretta (database errato ?). Questo è il motivo dell'errore.

Sembra che sia così come dici tu, ma non è così.
Ho anche detto prima che quella Select, così com'è, funzionava benissimo, quindi perchè dovrebbe essere Errato ora quello che, prima di mettere in pratica il modulo-dati, funzionava benissimo.

Comunque ad evitare ulteriori incomprensioni ho voluto provare a modificare la SELECT, scrivendo 
Codice: [Seleziona]
sql:= 'SELECT * FROM riepmovg'; 
e non ha funzionato, poi ho cambiato tabella e continua a NON funzionare.

In ogni caso, allego un'immagine in cui puoi constatare che i nomi di colonna e tabella sono corretti.

Ho guardato anche il percorso del db ('/media/dirdati/dativari/contabfam/ContabFamdb_prove') ed è corretto. Ho ricontrollato le proprietà degli oggetti ZEOS attuali e precedenti e, secondo me, anche quelle sembrano corrette.

Cosa potrei verificare ancora?


Titolo: Re:mie utility su database
Inserito da: petrusic - Agosto 31, 2022, 04:43:49 pm
Inghippo scoperto, almeno credo.

Nella procedura DataModuleCreate ho sospeso l'istruzione  "ZConnectionX1:= TZConnection.Create(nil);".
Ho riprovato e questa volta l'Errore logico sulla Select non è più comparso. Il programma è andato avanti bene.
Codice: [Seleziona]
implementation

{$R *.lfm}

{ TDataModule1 }
uses
  FrmMain;

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
//  ZConnectionX1:= TZConnection.Create(nil);        <--- refiuso della prima prova, fatta prima di aggiungere gli oggetti ZEOS
  ZConnectionX1.Database := FrmMain.dbCorr;
  ZConnectionX1.Connected:= True;
end;                           
Titolo: Re:mie utility su database
Inserito da: petrusic - Settembre 02, 2022, 11:08:16 pm
Bene, il ModuloDati comincia ad assumere una sua dimensione propria all'interno di uno dei miei programmi che accedono allo stesso DB.

Ritengo però che debba ancora migliorare la personalizzazione, perchè, come dicevo prima, i programmi in cui devo richiamare il ModuloDati sono già due, ma sicuramente aumenteranno di numero.

Attualmente per accedere al ModuloDati ho inseirto una use nella sezione Implementation, perchè il file ModuloDati.bas (cioè  UModuloDatiDB.pas) ed il corrispendente ModuloDati.lfm (cioè UModuloDatiDB.lfm) sono nella stessa directory del progetto
Codice: [Seleziona]
$ ls /media/dirdati/dativari/lazarus_progetti/lazarus_progetti_miei/DomusRatio
Appunti_programma    frm3.lfm  frmmain.lfm
backup               frm3.pas  frmmain.pas
bin                  frm4.lfm  griglia_e_matita.jpg
DomusRatioForm2.png  frm4.pas  grigliaMovvCto_2.png
DomusRatio.ico       frm5.lfm  grigliaMovvCto.png
DomusRatio.lpi       frm5.pas  grigliaMovv.png
DomusRatio.lpr       frm6.lfm  imgdef_domusRatio.png
DomusRatio.lps       frm6.pas  lib
DomusRatio.res       frm7.lfm  UModuloDatiDB.lfm
frm2_20211026.pas    frm7.pas  UModuloDatiDB.pas
frm2.lfm             frm8.lfm
frm2.pas             frm8.pas

Il ModuloDati, può contenere anche procedure o funzioni per la lettura di certi dati di uso ordinario da parte di più programmi (NON contemporaneamente attivi)?

La directory del ModuloDati può risiedere in un percorso superiore a quello del programma corrente (Es.: /media/dirdati/dativari/lazarus_progetti/lazarus_progetti_miei/)?

Io ho pensato di sì ed ho cercato di realizzare un collegamento dei programmi al ModuloDati comune, tramite una dichiarazione use nella sezione Interface di ciascun programma e l'aggiunta del nuovo percorso nella finestra dell'IDE "Progetto -> Opzioni Progetto -> Percorsi -> Altri file unit", ma ho incontrato diversi ostacoli dovuti a manifestazioni di Errori. Ho dovuto perciò fare marcia indietro e dedicarmi all'utilizzo del ModuloDati, intanto, nel progetto in cui sono impegnato, ma, dopo essere riuscito a compilarlo, senza errori, vorrei apportare le modifiche necessarie per poterlo impiegare anche in altri progetti, senza doverlo riscrivere all'interno di ciascun nuovo progetto.

Spero di avere chiarito il mio concetto.



Titolo: Re:mie utility su database
Inserito da: DragoRosso - Settembre 03, 2022, 11:18:19 am
l'aggiunta del nuovo percorso nella finestra dell'IDE "Progetto -> Opzioni Progetto -> Percorsi -> Altri file unit",

La procedura che hai fatto è corretta. I file interessati DEVONO ESSERE RIMOSSI DALLA DIRECTORY CORRENTE E SPOSTATI NELLA DIRECTORY INDICATA in "Altri file uint".

ma ho incontrato diversi ostacoli dovuti a manifestazioni di Errori.

Gli errori sono dovuti probabilmente ad una non completa esecuzione della procedura. In particolare dal tuo progetto corrente (ANALIZZATORE PROGETTI) devi togliere i due file del Datamodule.

L'inserimento dei due file nel progetto (devi eventualmente aggiungerli dal nuovo percorso) non è indispensabile, essendo file comuni a più progetti è meglio che non appaiano direttamente nella lista di alcun progetto (come fossero file di sistema di FPC).

Se hai problemi con la compilazione, prova ad inserirli manualmente nel progetto (non ho mai provato a condividere unita con form).

Ciao

Titolo: Re:mie utility su database
Inserito da: petrusic - Settembre 03, 2022, 04:02:08 pm
@ DragoRosso

Grazie per l'incoraggiamento.

Ho rimodificato il mio codice creando, all'interno della directory "/media/dirdati/dativari/lazarus_progetti/lazarus_progetti_miei"  la cartella "modul_dati_db" coi file pertinenti  al modulo-dati. Ho aggiunto la cartella fra i percorsi di ricerca dell'IDE --> Opzioni progetto -> Percorsi -> altri fil unit. Ho allineato tute le uses delle sezioni Interface dei vari file .pas del progetto ed ho compilato.

Dopo avere eseguito la compilazione senza errori, ho trasferito nel file "UModuloDatiDB.pas" del modulo-dati due funzioni che uso spesso, ottenendo il definitivo "UModuloDatiDB.pas":
Codice: [Seleziona]
unit UModuloDatiDB;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, ZCompatibility,
  ZConnection, ZDataset, SQLite3Conn, SQLDB, DB;


type

  { TDataModule1 }

  TDataModule1 = class(TDataModule)
  DataSourceX1: TDataSource;
  ZConnectionX1: TZConnection;
  ZQueryX1: TZQuery;
  ZReadOnlyQueryX2: TZReadOnlyQuery;
  ZReadOnlyQueryX1: TZReadOnlyQuery;

  procedure DataModuleCreate(Sender: TObject);
  function EstraiCoVoColleg(sql, segnal: String): String;
  function EstraiVocePianCont(sql: String): String;
 

private


public

end;

var
  DataModule1: TDataModule1;

implementation

{$R *.lfm}

{ TDataModule1 }
uses
  FrmMain;

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnectionX1.Database := FrmMain.dbCorr;
  ZConnectionX1.Connected:= True;
end;

function EstraiCoVoColleg(sql, segnal: String): String;
var

  estratto: String;

begin
  DataModule1.ZReadOnlyQueryX2.Active:= False;
  DataModule1.ZReadOnlyQueryX2.SQL.Text:= sql;
  DataModule1.ZReadOnlyQueryX2.Active:= True;
  if (DataModule1.ZReadOnlyQueryX2.RecordCount) > 0 then
  begin
    DataModule1.ZReadOnlyQueryX2.First;
    case segnal of
      'S', 'P':
        begin
          estratto:= DataModule1.ZReadOnlyQueryX2.FieldByName('NumVoColleg').AsString;
        end
      else begin
        estratto:= '*?*';   // CODICE DI errore  PER record NON TROVATO
      end;
    end;
  end;
  Result:= estratto;
end;

function EstraiVocePianCont(sql: String): String;    // Estrae la Voce contabile, a cui accoda il contenuto del campo "ContrPartSiNo"
var
  swOpenErro: Boolean = False;

  estratto: String;

begin
  WriteLn('function EstraiVocePianCont');
  DataModule1.ZReadOnlyQueryX2.Active:= False;
  DataModule1.ZReadOnlyQueryX2.SQL.Text:= sql;
  DataModule1.ZReadOnlyQueryX2.Active:= True;
  if (DataModule1.ZReadOnlyQueryX2.RecordCount) > 0 then
  begin
    DataModule1.ZReadOnlyQueryX2.First;
    estratto:= DataModule1.ZReadOnlyQueryX2.FieldByName('NomeVoce').AsString;
    estratto:= estratto + DataModule1.ZReadOnlyQueryX2.FieldByName('ContrPartSiNo').AsString;
  end
  else begin
    estratto:= '*?*';   // CODICE DI errore  PER recor NON TROVATO
  end;
  Result:= estratto;
end;
end.

Ebbene, la chiamata di una qualsiasi delle funzioni appena spostate, produce il seguente messaggio d'Errore:
Citazione
frm6.pas(560,23) Error: Identifier not found "EstraiVocePianCont"
frm6.pas(570,36) Error: Identifier not found "EstraiCoVoColleg"

Non capisco perchè vengono ignorate. Eppure avevo percepito che avrei potuto aggiungere nel modulo-dati anche funzioni e procedure.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Settembre 03, 2022, 05:49:53 pm
@ DragoRosso


Codice: [Seleziona]
......
procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnectionX1.Database := FrmMain.dbCorr;
  ZConnectionX1.Connected:= True;
end;

function TDataModule1.EstraiCoVoColleg(sql, segnal: String): String;
var

  estratto: String;

begin
  DataModule1.ZReadOnlyQueryX2.Active:= False;
  DataModule1.ZReadOnlyQueryX2.SQL.Text:= sql;
  DataModule1.ZReadOnlyQueryX2.Active:= True;
  if (DataModule1.ZReadOnlyQueryX2.RecordCount) > 0 then
  begin
    DataModule1.ZReadOnlyQueryX2.First;
    case segnal of
      'S', 'P':
        begin
          estratto:= DataModule1.ZReadOnlyQueryX2.FieldByName('NumVoColleg').AsString;
        end
      else begin
        estratto:= '*?*';   // CODICE DI errore  PER record NON TROVATO
      end;
    end;
  end;
  Result:= estratto;
end;

function TDataModule1.EstraiVocePianCont(sql: String): String;    // Estrae la Voce contabile, a cui accoda il contenuto del campo "ContrPartSiNo"
var
  swOpenErro: Boolean = False;

  estratto: String;

begin
  WriteLn('function EstraiVocePianCont');
  DataModule1.ZReadOnlyQueryX2.Active:= False;
  DataModule1.ZReadOnlyQueryX2.SQL.Text:= sql;
  DataModule1.ZReadOnlyQueryX2.Active:= True;
  if (DataModule1.ZReadOnlyQueryX2.RecordCount) > 0 then
  begin
    DataModule1.ZReadOnlyQueryX2.First;
    estratto:= DataModule1.ZReadOnlyQueryX2.FieldByName('NomeVoce').AsString;
    estratto:= estratto + DataModule1.ZReadOnlyQueryX2.FieldByName('ContrPartSiNo').AsString;
  end
  else begin
    estratto:= '*?*';   // CODICE DI errore  PER recor NON TROVATO
  end;
  Result:= estratto;
end;
end.

Ebbene, la chiamata di una qualsiasi delle funzioni appena spostate, produce il seguente messaggio d'Errore:
Citazione
frm6.pas(560,23) Error: Identifier not found "EstraiVocePianCont"
frm6.pas(570,36) Error: Identifier not found "EstraiCoVoColleg"

Non capisco perchè vengono ignorate. Eppure avevo percepito che avrei potuto aggiungere nel modulo-dati anche funzioni e procedure.

NELLA IMPLEMENTATION: DEVI IMPLEMENTARE LE FUNZIONI CON L'INTESTAZIONE "TDATAMODULE1." !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Visto che appartengono a quella classe.

Titolo: Re:mie utility su database
Inserito da: petrusic - Settembre 03, 2022, 10:50:21 pm
NELLA IMPLEMENTATION: DEVI IMPLEMENTARE LE FUNZIONI CON L'INTESTAZIONE "TDATAMODULE1." !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Visto che appartengono a quella classe.
Ho fatto come mi hai indicato, ma il messaggio d'Errore ricompare tale e quale:
Codice: [Seleziona]
unit UModuloDatiDB;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, ZCompatibility,
  ZConnection, ZDataset, SQLite3Conn, SQLDB, DB;


type

  { TDataModule1 }

  TDataModule1 = class(TDataModule)
  DataSourceX1: TDataSource;
  ZConnectionX1: TZConnection;
  ZQueryX1: TZQuery;
  ZReadOnlyQueryX2: TZReadOnlyQuery;
  ZReadOnlyQueryX1: TZReadOnlyQuery;

  procedure DataModuleCreate(Sender: TObject);
  function EstraiCoVoColleg(sql, segnal: String): String;
  function EstraiVocePianCont(sql: String): String;

private

public

end;

var
  DataModule1: TDataModule1;

implementation

{$R *.lfm}

{ TDataModule1 }

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnectionX1.Connected:= True;
end;

function TDataModule1.EstraiCoVoColleg(sql, segnal: String): String;    // Estrae dalla Tabella "racodvoci" il codice di sottoconto contrappoto al tipo inmdicato in "segnal":
//                                                           se "segnal = "S" o "P", la viariabile "coVoSotCto" contiene un codice di Sottoconto di Cassa
//                                                           se "segnal = "N", la viariabile "coVoSotCto" contiene un codice di Sottoconto di Contropartita
var

  estratto: String;

begin
  DataModule1.ZReadOnlyQueryX2.Active:= False;
  DataModule1.ZReadOnlyQueryX2.SQL.Text:= sql;
  DataModule1.ZReadOnlyQueryX2.Active:= True;
  if (DataModule1.ZReadOnlyQueryX2.RecordCount) > 0 then
  begin
    DataModule1.ZReadOnlyQueryX2.First;
. . .
end;

function TDataModule1.EstraiVocePianCont(sql: String): String;    // Estrae la Voce contabile, a cui accoda il contenuto del campo "ContrPartSiNo"
var
  swOpenErro: Boolean = False;

  estratto: String;

begin
. . .
Citazione
frm6.pas(614,19) Error: Identifier not found "EstraiVocePianCont"
frm6.pas(627,23) Error: Identifier not found "EstraiVocePianCont"
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Settembre 04, 2022, 12:27:14 am
Non puoi usare in quel modo le due funzioni. Appartengono ad una classe e devi usarle con l'istanza della classe:

Codice: [Seleziona]
 strimia := classe.EstraiVocePianCont(sql);
Titolo: Re:mie utility su database
Inserito da: petrusic - Settembre 04, 2022, 10:30:01 am
Non puoi usare in quel modo le due funzioni. Appartengono ad una classe e devi usarle con l'istanza della classe:

Codice: [Seleziona]
 strimia := classe.EstraiVocePianCont(sql);
L'avevo provata pure, ma mi da sempre Errore:
Codice: [Seleziona]
striMia:= TDataModule1.EstraiVocePianCont(sql);          <---  così da l'Errore riportato sotto
                     
Citazione
frm6.pas(614,55) Error: Only class methods, class properties and class variables can be accessed in class methods
frm6.pas(614,55) Error: Only class methods, class properties and class variables can be referred with class references


Però
Codice: [Seleziona]
striMia:= DataModule1.EstraiVocePianCont(sql);            <---  così funziona
                     

Titolo: Re:mie utility su database
Inserito da: petrusic - Settembre 04, 2022, 11:53:13 am
Purtroppo non è finita.

Ho ancora difficoltà perchè non riesco ad impostare il percorso del mio database.
Nel modulo-dati, avevo  impostato il percorso così:
Codice: [Seleziona]
uses
  FrmMain;

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  ZConnectionX1.Database := FrmMain.dbCorr;
  ZConnectionX1.Connected:= True;
end;

E funzionava, però mi sono reso conto che il modulo dati rimaneva agganciato al progetto utente per via della "uses FrmMain", dove sono in grado di capire quale database fra (dbOrig e dbProve) è quello attivo.

Ho voluto perciò togliere quel vincolo. Per farlo, ho pensato di definire nel modulo-dati una variabile (UdbCorr) da valorizzare nella procedure TForm1.FormCreate del progetto utente.

dichiarazione variabile nel modulo dati:
Codice: [Seleziona]
unit UModuloDatiDB;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, ZCompatibility,
  ZConnection, ZDataset, SQLite3Conn, SQLDB, DB;


type

  { TDataModule1 }

  TDataModule1 = class(TDataModule)
  DataSourceX1: TDataSource;
  ZConnectionX1: TZConnection;
  ZQueryX1: TZQuery;
  ZReadOnlyQueryX2: TZReadOnlyQuery;
  ZReadOnlyQueryX1: TZReadOnlyQuery;

  procedure DataModuleCreate(Sender: TObject);
  function EstraiCoVoColleg(sql, segnal: String): String;
  function EstraiVocePianCont(sql: String): String;
 

private

public
var
  UdbCorr: String;

end;

var
  DataModule1: TDataModule1;


implementation

{$R *.lfm}

{ TDataModule1 }
//uses
//  FrmMain;


procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
//  ZConnectionX1.Connected:= False;
//  ZConnectionX1.Database := FrmMain.dbCorr;
  ZConnectionX1.Database := UdbCorr;
  ZConnectionX1.Connected:= True;
end;                                                 

Poi nel progetto utente ho scritto:
Codice: [Seleziona]
procedure TForm1.FormCreate(Sender: TObject);
var
begin
. . .
//------------------------------------ Assegno percorso per l'accesso al DB corretto
  dbCopia:= '/media/dirdati/dativari/contabfam/ContabFamdb(copia)';       
  dbOrig:= '/media/dirdati/dativari/contabfam/ContabFamdb';
  dbProve:= '/media/dirdati/dativari/contabfam/ContabFamdb_prove';     
//------------------------------------------------------------------------------------
  DataModule1.UdbCorr:= dbProve;                             
Ebbene, ques'ultima istruzione provoca il seguente Errore:
Citazione
Il progetto DomusRatio ha sollevato una eccezione di classe 'External: SIGSEGV'.
 All'indirizzo 42A20E
Ho provato a spostare la dichiarazione della variabile nel modulo-dati ma inutilmente. Anche questa volta  non ho capito come risolvere.
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Settembre 04, 2022, 02:10:33 pm
TI HO GIA' SCRITTO IN UN PRECEDENTE POST CHE NON PUOI CHIAMARE IL DATAMODULE DALLA FORM CREATE !!!!

Quella classe non è ancora stata istanziata. PER L'ENNESIMA VOLTA TORNO A RIPETERTI CHE DEVI FARE ATTENZIONE ALLA SEQUENZA DI CREAZIONE DEGLI OGGETTI.
Titolo: Re:mie utility su database
Inserito da: Stilgar - Settembre 07, 2022, 08:29:42 am
In linea di principio hai ragione, se è l'ide a gestire l'ordine di creazione nel file principale.


Poi si può sempre intervenire e alterare l'ordine di creazione da ide o (come preferisco) a manina.




Stilgar
Titolo: Re:mie utility su database
Inserito da: DragoRosso - Settembre 07, 2022, 09:06:10 am
Volendo proprio dire, io preferisco istanziare a "mano" tutto quello che mi serve "subito", per esempio i datamodule io li istanzio sempre a mano in quanto normalmente dai DB leggo parte della configurazione del software.

Alterare la sequenza di "inizializzazione" delle unità non sposta il problema dell'attenzione alla creazione degli oggetti, inoltre ha degli effetti secondari non trascurabili come ad esempio spostare la "FORM" di riferimento dell'applicazione (Form che quando chiusa provoca automaticamente la chiusura dell'applicazione). Inoltre c'è da tenere presente che "Thread Principale", "Pompa dei Messaggi" e altro seguono la Form principale, Form che per definizione  è la prima nella sequenza di creazione e che ha appunto alcune peculiarità tipiche.

Comunque và adattato il proprio modo di programmare sulla base dei principi logici dell'applicazione e avendo ben chiare alcune regole basilari della programmazione ad oggetti e di come l'ambiente runtime gestisce le varie fasi di avvio di una applicazione.

Come ultima nota, prima di scrivere codice sarebbe utile farsi un diagramma di flusso con le varie fasi di attività dell'applicazione, cosa che oltre che consigliato e direi obbligatorio per chiunque si occupi di programmazione aiuta sicuramente ad organizzare poi il codice.

Ciao