ADO-Komponente in Klasse verwenden scheitert



  • Hallo zusammen,
    ich bekomme es nicht hin ADO Komponenten in meine Klasse (Konsolenanwendung) einzubauen. Es erscheinen "unresolved external" Linkerfehler im Bezug auf Funktionen innerhalb der ADO Komponentenklassen. Die richtigen Headerdateien habe ich auch eingebunden. Sieht jemand von euch was ich hier falsch mache?

    #include <vcl.h>
    #include <windows.h>
    #include <Data.DB.hpp>
    #include <Data.Win.ADODB.hpp>
    
    class CasraStaffInterface
    {
    
    	private:
    	String        Hostname, AppPath, ConnectionString, SQLCommand;
    	TStringList   *ResultList;
    	TADOConnection *ADOConnection;
    	TADOQuery *ADOQuery;
    
        void SendEmail();
    	void Log(String);
    	String Computername();
    
    	public:
    	CasraStaffInterface();
    	~CasraStaffInterface();
    
    	int Execute();
    
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    
    	int returnvalue;
    	CasraStaffInterface csi;
    
    	returnvalue = csi.Execute();
    
    	return returnvalue;
    
    }
    //------------------------------------------------------------------------------
    CasraStaffInterface::CasraStaffInterface()
    {
    		AppPath = GetCurrentDir();
    
    		ConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Db;Data Source=serv-casra";
    		SQLCommand = "SELECT xxxxxxxxxxxxxxxxxxx";
    
    		ResultList = new TStringList();
    		ADOConnection = new TADOConnection(Application);
    		ADOQuery = new TADOQuery(Application);
    
    }
    //------------------------------------------------------------------------------
    CasraStaffInterface::~CasraStaffInterface()
    {
    
    		delete ResultList;
    		delete ADOQuery;
    		delete ADOConnection;
    
    }
    //------------------------------------------------------------------------------
    int CasraStaffInterface::Execute()
    {
    
    	  int returnvalue;
    
    	 //----Component Setup
    	  ADOConnection->ConnectionString = ConnectionString;
    	  ADOConnection->LoginPrompt = false;
    	  ADOConnection->ConnectionTimeout = 3;
    	  ADOQuery->CommandTimeout = 3;
    	  ADOQuery->Prepared = true;
    	  ADOQuery->SQL->Clear();
    	  ADOQuery->SQL->Add(SQLCommand);
    	  ADOQuery->Connection = ADOConnection;
    	 //----------------------------------------
    
    	try
    	{
    		  ADOQuery->Open();
    		  ADOQuery->First();
    
    		  while(!ADOQuery->Eof)
    		  {
    
    			 String Fieldset = "";
    			 for(int i = 0; i < ADOQuery->Fields->Count; i++)
    			 {
    				Fieldset = Fieldset + ADOQuery->Fields->Fields[i]->AsString + ";";
    			 }
    
    			 ResultList->Add(Fieldset);
    			 ADOQuery->Next();
    		  }
    
    		  ResultList->SaveToFile(AppPath + "\\CasraStaff.csv");
    		  returnvalue = 0;
    
    	}
    	catch (Exception &ex)
    	{
    		   Log(ex.ToString());
    		   returnvalue = 1;
    	}
    
    	return returnvalue;
    }
    


  • Linkerfehler bedeuten, daß Libraries (bzw. Packages) bei deinem Projekt fehlen.



  • Danke für die rasche Antwort. Wenn ich ohne Klasseninitialisierung in der "int main" kompiliere

    CasraStaffInterface csi;
    
    returnvalue = csi.Execute();
    

    erscheinen diese Fehler nicht und das Programm wird anstandslos kompiliert. Passt dieses Verhalten ebenfalls zu fehlenden Packages?



  • Das sollte die Lösung sein:

    You need to edit the .cbproj file and manually add "adortl.lib" to the lists
    of .lib files and "adortl.bpi" to the lists of .bpi file (for good measure).
    You can find the section to add these libraries by searching for "rtl.lib"
    or "vcl.lib" and add "adortl.lib" appropriately.

    Werde es morgen auf der Arbeit testen



  • Funktioniert.

    Möchte man eine Standalone-EXE kompilieren und und entfernt die bekannten 2 Häkchen in den Projektoptionen, muss man noch die dbrtl.lib einfügen.



  • Hallo,

    du mußt aber nicht manuell die Projektdateien bearbeiten, sondern kannst dies über die Projektoptionen einstellen: Loading Packages in an Application

    Hier noch ein Link zum generellen Umgang mit Packages: Working with Packages and Components - Overview



  • Vielen Dank



  • Ich habe die nötige adortl.lib mit

    #pragma comment(lib,"adortl.lib")
    

    eingebunden. 😉



  • Bei mir hatte es Anfangs schon gehapert überhaupt alle notwendigen Libs zu benennen. ADO ist absolutes Neuland für mich. Über ein pragma einzubinden ist natürlich auch nicht schlecht wenn man den Quelltext 1:1 kompilierbar weitergeben möchte. Meine geliebte Indy SMTP Komponente habe ich auf gleiche weise eingebunden um das Reporting der Schnittstelle per Email zu realisieren. Auch dieses funktioniert wunderbar.

    Natürlich ist das Projekt nicht so unsauber dahingeschludert geblieben wie oben gezeigt. Dabei ergab sich mir noch eine Frage, auch wenn ich jetzt Gefahr laufe, den eigenen Thread zu hijacken:

    ADOQuery->Open();
    
    while(!ADOQuery->Eof) .....
    

    Wäre es nicht sauberer die while durchrattern zu lassen wenn ich in einem Event signalisiert bekomme, dass die SQL Abfrage vom Datenbankserver vollständig verarbeitet und die Felder an die ADOQuery Komponente übermittelt wurden? Die SQL-Abfrage ist riesig und wird nach ca. einem Jahr an die 600.000 Datensätze durchforsten (geschätzte Dauer bestimmt 20 min). Ich kann mir schwer vorstellen das SQLQuery->Open() so lange blockiert (zumal void Funktion) bis die Query vollständig verarbeitet wurde und es erst dann mit der while weiter geht...oder täusche ich mich da?



  • Hi Zero01,

    ADO ist kein Hexenwerk, sondern eine ausgereifte einfach zu bedienende Datenbankschnittstelle, die bei Borlands Nachfahren problemlos funktioniert.
    Für die Frage, was alles reingeladen werden muss, probiere das erst mal mit normalen Formularanwendungen aus. Ich selber arbeite zwar zur Zeit nur noch mit Delphi, aber bei C++ ist es genau so.
    Erstelle Dir ein Fomular, das alle Teile die Du sonst noch brauchst enthält, außer den Ado-Componenten. Dann übersetze das Projekt und gucke, was alles an Headderdateien reingeladen wurde. Speichere Dir die in einen Kommentar, ziehe Dir die ADO-Komponenten drauf und übersetze noch mal. Und dann vergleiche die Include-Bereiche. Bei mir ist lediglich
    include <Data.Win.ADODB.hpp>
    dazugekommen, die macht dann den Rest.
    Aber noch ein Hinweis zu den von Dir verwendeten Komponenten. Vergiss ADOQuery und ADOTable. Das sind nur mehr oder weniger lieblos an die BDE-Componenten angepasste Typen.
    Für alles was irgendwie eine Datenmenge zurückgibt nimm TADODataset, und für alles andere TADOCommand.
    Das sind die richtigen originalen Komponenten, die auch gut und zuverlässig arbeiten.
    Ansonsten würde ich versuchen, mal mit verschiedenen Varianten rumzuspielen. Zum einen würde ich den jeweils pro Zeile auszugebenden String nach möglichkeit mit einer geeigneten SQL-Anweisung für die gesamte Zeile zusammenzubauen. Dann eventuell mit clUseServer bzw clUseClient bei Kursorlocation experimentieren. Eventuell auch verschiedene Cursortypen ausprobieren... Möglich dass ctOpenForwardOnly die Sache etwas beschleunigt... Eventuell auch ein TClientdataset anbinden, dass dann direkt nach .csv exportieren kann.
    Aber 600.000 Datensätze sind eigentlich nicht das Problem, an dem ADO scheitern sollte. Wenn Du wärend dem einlesen schon verarbeiten willst, dann müsste das über asynchrones Datenlesen gehen. Da bleibt nur ausprobieren. Der Schlüssel dafür müsste ExecuteOptions sein. Mit ExecuteComplete kannst Du irgendwie testen, ob schon alles gelesen wurde.
    Insgesamt wirst Du aber nicht darum herum kommen, Dich in die Thematik etwas einzulesen. Auch wenn es nicht C++ ist, so kann ich Dir doch das Buch "ADO und Delphi" von Andreas Kosch wärmstens ans Herz legen. Im Augenblick ist das im Preis rech heftig, aber vielleicht kannst Du es ja irgendwo ausborgen. Nicht daran stören lassen, dass es schon ein wenig aus der Computer-Steinzeit stammt (2002), da ADO seit der zeit nicht wesentlich weiter entwickelt wurde trifft das alles noch bestens zu, und das Buch geht wirklich bestens auf alles ein.
    Wenn es aber in dem Fall nur darum geht, mit höchstmöglicher Geschwindigkeit die gesamten Daten einmal auszulesen um ein csv daraus zu machen, dann solltest Du eventuell auch mal über dbExpress nachdenken. Das ist für solche Hochleistungsverarbeitung ohne jeglichen Kopfort, dafür aber mit höchstmöglichem Tempo eigentlich maßgeschneidert.
    Falls Du nicht nur den C++Builder sondern auch Delphi hast, empfiehlt es sich vielleicht, das Ausprobieren auf der Delphi-Ebene zu machen, das ist für nur so zum rumprobieren einfach schneller und handlicher.

    Gruß Mümmel



  • Vielen Dank muemmel, du hast mir sehr geholfen! Momentan habe ich die Kapitel über synchrones bzw. asynchrones Verarbeiten durch und arbeite gerade die Events der Komponenten durch. Die Applikation als solches ist bereits in einer ersten Alpha Version implementiert und verrichtet schon gute Arbeit für uns.



  • Hi Zero01

    Zero01 schrieb:

    Vielen Dank muemmel, du hast mir sehr geholfen! Momentan habe ich die Kapitel über synchrones bzw. asynchrones Verarbeiten durch und arbeite gerade die Events der Komponenten durch. Die Applikation als solches ist bereits in einer ersten Alpha Version implementiert und verrichtet schon gute Arbeit für uns.

    Hast Du mal probiert, ob es einen Unterschied macht, ob Du die einzelnen Felder einzeln ausliest und zu nem CSV-Strijng zusammensetzt, oder den fertigen String mit ner SQL-Anweisung zusammenbaust (in Access etwa "select Feld1 & ';' & Feld2 & ';' & ...") und nur das eine Feld ausliest?

    Gruß Mümmel



  • Hallo muemmel,
    nein, soweit konnte ich nicht gehen da sich der String des weiteren aus anderen Zeichenketten hinzufügend zusammensetzt, die auf Basis der ausgelesenen Daten zuteils generiert werden, eine Art externes Dictionary oder Umsetztabelle mit Mitarbeiterstammdaten aus einer ganz anderen Datenbank, wie auch immer man dies nennen mag. Der abzufragende DB-Server beherbergt ein MSSQL 2014 SP2 was aufgrund der Hauptapplikation nicht veränderbar ist.



  • Hi Zero01,

    MS-SQL arbeitet ja gut mit ADO zusammen. Probiere da auf jeden Fall mal noch ein bisschen mit dem Cursortype. ctOpenForwardOnly müsste die schnellste Variante sein. (aus als Firehose-Cursor bezeichnet).
    Falls Du dabei Daten ändern musst, eventuell MarshalOptions auf moMarshalModifiedOnly seztzen, damit nur die geänderten zurückgesendet werden.
    Wahrscheinlich ist es auch schneller, wenn Du mit festen Feldern arbeitest.
    Auch möglichst nicht den Conektion-String direkt in das Dataset schreiben, sondern ne TADOConnection verwenden. Dort dann noch bei ConnectOptions auf coAsyncConnect stellen und Mode eventuell auf cmRead stellen und wie schon gesagt, ausprobieren, was bei Cursorlocation schneller ist, clUseServer oder clUseclient. Wenn der SQL-Server auf der gleichen physischen Maschine läuft auf jeden fall clUseServer versuchen. Und wenn nicht ausprobieren.

    Gruß Mümmel


Anmelden zum Antworten