Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Prism Allgemeiner Datenbankzugriff unter .NET (https://www.delphipraxis.net/70050-allgemeiner-datenbankzugriff-unter-net.html)

Alexander 24. Mai 2006 11:14

Datenbank: Alle :) • Zugriff über: ADO.NET

Allgemeiner Datenbankzugriff unter .NET
 
Hallo,
die Datenbankverbindung unter .NET ist ja eigentlich recht einfach, wenn man ADO.NET mit den entsprechenden Providern nutzt. Ich habe das gerade mal alles in eine Methode gepackt:
Delphi-Quellcode:
procedure TWinForm1.Button3_Click(sender: System.Object; e: System.EventArgs);
var
  cmd : OdbcCommand;
  con : OdbcConnection;
  dataAdapter : OdbcDataAdapter;
  DSet : DataSet;
begin
  con := OdbcConnection.Create('DSN=wall;UID=wwwrun;PWD=');
  cmd := con.CreateCommand();
  cmd.CommandText := 'SELECT * FROM WetterData';
  dataAdapter := OdbcDataAdapter.Create(cmd);
  DSet := DataSet.Create;

  dataAdapter.Fill(DSet);

  DataGrid1.DataSource := DSet;
end;
Hier greife ich ja noch über das alte ODBC auf die Datenbank zu. Dieser Weg ist ja gerade unter .NET nicht sehr optimal. Ein direkte nativer Zugriff mit den entsprechenden .NET Providern ist da ja besser und funktioniert quasi genau so. Z.B. muss ich als Datentyp ja nur den MySQLConnection oder SQLConnection statt ODBCConnection nehmen.
Ich suche aber eine Möglichkeit das allgemein zu halten und bspw. per Konfigurationsdatei wählen welche Verbindung ich nehmen möchte.
Ich habe gesehen, dass alle Klassen von einer Klasse abgeleitet wurden, bei der Connection-Klasse z.b. von IDbConnection. Also muss man mein Vorhaben irgendwie realisieren können. Hinzu kommt, dass wenn ich z.B. den MySQL-Provider direkt nehmen würde, dieser auch auf dem Zielrechner installiert sein muss. Ist dieser nicht installiert, aber in dem Programm genutzt, müsste es also zu einer Exception kommen. Es wäre also noch genialer, wenn man diese dynamisch laden könnte. Ich stelle mir das so vor:
In einem String steht z.B. Firebird oder MySQL, dann wird der entsprechende Provider verwendet, obwohl dieser nicht explizit im Quelltext genannt ist.
Ich hoffe ihr habt mich verstanden und könnt mir helfen :). Für den einen oder anderen .NET Crack ist das sicherlich kein Problem ;).
PS: Bitte beachtet, dass ich gerade erst mit .NET angefangen habe zu spielen :zwinker:

Nachtrag: Zur Verdeutlichung suche ich also eine Funktion oder Methode, die mir ein DataSet zurückgibt:
Delphi-Quellcode:
function getDataSet(conString, Art, sql : String) : DataSet;
begin ... end;
Wobei Art angeben soll, wie zur Datenbank verbundne werden soll und welche überhaupt genommen wird. Also z.B. Art := MySQL;
SQL gibt das SQL-Statement an.
Nur so als Verdeutlichung ;)

r_kerber 24. Mai 2006 11:44

Re: Allgemeiner Datenbankzugriff unter .NET
 
Zitat:

Zitat von Alexander
Hinzu kommt, dass wenn ich z.B. den MySQL-Provider direkt nehmen würde, dieser auch auf dem Zielrechner installiert sein muss. Ist dieser nicht installiert, aber in dem Programm genutzt, müsste es also zu einer Exception kommen.

Den Provider mußt Du dann einfach mitliefern (wie früher die BDE). Das klappt sogar auf Web-Servern.
Ansonsten würde mir auf Anhieb BDP einfallen. Aber auch dann mußt die BDP-Assembly und den benötigten BDP-Provider mitliefern. Aber BDP ist leider nicht unumstritten (kannst Du auch hier in der DP nachlesen).

mquadrat 24. Mai 2006 12:10

Re: Allgemeiner Datenbankzugriff unter .NET
 
Delphi-Quellcode:
type TDbConnectionFactory = class

  public
    class function getConnection(aConStr, aArt: string): IDbConnection;

end;

...

class function getConnection(aConStr, aArt: string): IDbCOnnection;
begin

  if (aArt = 'MySQL') then
     result := MySqlConnection.Create(aConStr)
  else if (aArt = 'Firebird') then
     result := FbConnection.Create(aConStr)
  else if (aArt = 'ODBC') then
     result := OdbcConnection.Create(aConStr);

  if (result = nil) then
    Exception.Create('Ungültige Datenbankart');

end;
Delphi-Quellcode:
  myConnection := TDbConnectionFactory.getConnection('DSN=wall;UID=wwwrun;PWD=', 'ODBC');
  ...
Das würd ich machen. Entsprechende Factorys eben auch für die anderen Sachen (z.B. Command)

Alexander 24. Mai 2006 12:27

Re: Allgemeiner Datenbankzugriff unter .NET
 
Zunächst erstmal vielen Dank an euch beiden :)
@r_kerber Klar, dass ich die Provider irgendwie mitliefern muss, wenn die genutzt werden sollen.

@mquadrat So etwas habe ich mir auch schon gedacht. Bisher habe ich es nicht ausprobiert, aber würde es krachen, wenn der MySQL-Provider nicht installiert ist, dieser im Code wie bei dir aber definiert ist, aber nicht verwendet wird? Sprich der Enduser möchte Firebird nutzen, braucht also nicht den MySQL-Provider zu installieren. Es wird also getConnection(..., 'Firebird') aufgerufen.

Außerdem stört mich noch eine Sache (daher habe ich diese Idee auch wieder verworfen gehabt) und zwar ist das ja nicht gerade dynamisch. Z.B. wurde mein Programm gerade installiert. Jetzt soll aber plötzlich zu einer ganz anderen Datenbank connected werden, die nicht in der Methode getConnection definiert ist. Diese soll aber dennoch unterstützt werden.
Ist das irgendwie möglich?

mquadrat 24. Mai 2006 12:43

Re: Allgemeiner Datenbankzugriff unter .NET
 
Dann müsstest du die Assembly des gewünschten Providers von Hand laden und die jeweiligen Klassen suchen gehen, die Namen kennst du ja nicht.

Um eine Assembly zu laden brauchst du entweder den Dateinamen oder den entsprechenden String mit dem die Assembly im GAC gefunden werden kann. Sprich der Benutzer müsste in einer Config-Datei eins von beiden eintragen oder du stellst die GAC Strings zur Verfügung und der Nutzer hakt nur an.

Die eigentliche Implementierung wäre dann trotzdem mittels einer Factory, die als Singleton implementiert ist. Die würde dann im Konstruktor die Config-Datei lesen, die entsprechende Assembly laden und die Typnamen raussuchen.

Alexander 24. Mai 2006 12:55

Re: Allgemeiner Datenbankzugriff unter .NET
 
Du hast nicht zufällig ein Beispiel wie man die GAC's lädt und solch ein Assembly dynamisch lädt und verwendet?

Neelix 24. Mai 2006 13:04

Re: Allgemeiner Datenbankzugriff unter .NET
 
Zitat:

Zitat von Alexander
Du hast nicht zufällig ein Beispiel wie man die GAC's lädt und solch ein Assembly dynamisch lädt und verwendet?

Wenn die Assembly z.B. im selben Pfad liegt, wie die Assemblys des Programmes, dann wird sie dynamisch geladen. Ein Laden in den GAC ist nicht unbedingt erforderlich (XCOPY-Installation). Dafür gäbe es dann aber das kleine Progrämmchen gacutil. das mußt Dhne Parameter starten, dann siehst Du was Du eingeben mußt.
Zitat:

Zitat von Alexander
Jetzt soll aber plötzlich zu einer ganz anderen Datenbank connected werden, die nicht in der Methode getConnection definiert ist. Diese soll aber dennoch unterstützt werden.
Ist das irgendwie möglich?

IMHO mußte sowohl bei der BDE als auch bei ODBC, ADO, dbExpress, ... immer der passende DB-Treiber mitgeliefert werden. Häufig mußte auch der jeweilige DB-Client ebenfalls auf dem lokalen PC installiert sein. Das hat sich bei .Net nicht geändert. Auch "unter der Haube" von BDP befinden sich diverse DB-spezifische BDP-Provider.

Alexander 24. Mai 2006 13:36

Re: Allgemeiner Datenbankzugriff unter .NET
 
Das Tool scheint da wirklich zu helfen:
Ic habe mir gerade mal alle aufgelistet Assemblies aufgelistet. Z.B. war dabei:
Zitat:

MySql.Data, Version=1.0.7.30071, Culture=neutral, PublicKeyToken=c5687fc88969c44d, Custom=null
SCHEME: <WINDOWS_INSTALLER> ID: <MSI> DESCRIPTION : <Windows Installer>
MySql.Data, Version=1.0.7.30072, Culture=neutral, PublicKeyToken=c5687fc88969c44d, Custom=null
SCHEME: <WINDOWS_INSTALLER> ID: <MSI> DESCRIPTION : <Windows Installer>
So jetzt kann man ja mal annehmen, dass der MySQL-Treiber bei der Entwicklung nicht mit eingebungen war. Dies soll jetzt dynamisch nachgeholt werden.
Ich schätze dass ich den String MySQL.Data benötige. Wie muss ich jetzt weitermachen?

Ich habe also:
Delphi-Quellcode:
class function getConnection(aConStr, aArt: string): IDbCOnnection;
begin

  if (aArt = 'neuerProvider') then
  begin
     `hier müsste jetzt der MySQL Treiber irgendwie geladen werden...
  end
  else if (aArt = 'Firebird') then
     result := FbConnection.Create(aConStr)
  else if (aArt = 'ODBC') then
     result := OdbcConnection.Create(aConStr);

  if (result = nil) then
    Exception.Create('Ungültige Datenbankart');

end;

mquadrat 26. Mai 2006 11:26

Re: Allgemeiner Datenbankzugriff unter .NET
 
Schau dir mal die Links an

Assembly.Load Method

Finden von Typen in einer Assembly

Zusammengefasst musst du, wie du schon erkannt hast den, Assemblynamen (AssemblyName Class) haben, damit du die Assembly laden kannst, dann alle in der Assembly vorhanden Typen durchsuchen bis du die Klasse findest, die von IDbConnection erbt und diese dann instanziieren.

Alexander 26. Mai 2006 14:00

Re: Allgemeiner Datenbankzugriff unter .NET
 
Danke, ich hoffe ich kriege es hin ;)

Alexander 31. Mai 2006 16:08

Re: Allgemeiner Datenbankzugriff unter .NET
 
Hallo,
ich muss das Thema noch mal hervorholen. Ich habe noch 2 Probleme bzw. Fragen.
So folgenden Code nutze ich:
Delphi-Quellcode:
class function TSQLHelper.getDataAdapter(aArt: string): IDbDataAdapter;
begin
  if (aArt = 'MySQL') then
     result := MySqlDataAdapter.Create()
  else if (aArt = 'Firebird') then
     result := nil// FbConnection.Create(aConStr)
  else if (aArt = 'SQL-Server') then
     result := SqlDataAdapter.Create()
  else if (aArt = 'Access') then
     result := OdbcDataAdapter.Create()   // Wie geht's bei Access?
  else if (aArt = 'Datenbank über ODBC') then
     result := OdbcDataAdapter.Create();
  if (result = nil) then
    Exception.Create('Ungültige Datenbankart');
end;

class procedure TSQLHelper.getDataSet(var Res : DataSet; sql, ConStr, aArt : String);
var
  cmd : IDbCommand;
  con : IDbConnection;
  dataAdapter : IDbDataAdapter;
  DSet : DataSet;
begin
  con := getConnection(ConStr, aArt);
  cmd := con.CreateCommand();
  cmd.CommandText := sql;
  dataAdapter := getDataAdapter(aArt);
  dataAdapter.SelectCommand := cmd;

  dataAdapter.Fill(Res);
end;
Die Methode getConnection ist eigentlich wie getDAtaAdapter aufgebaut und poste ich der Übersichtlichkeit halber mal nicht.
Momentan teste ich nur mit ODBC, auch das dynamische Laden habe ich noch nicht integriert.
Das Dataset Res, das der Methode getDataSet übergeben wird, wird quasi gefüllt und ist mit dem DataGrid verbunden.
Doch wie kann ich die Daten in eine bestimmte Table des DataSets speichern?
Außerdem wäre es interessant, wie ich dann genau die DataSEt.Table im DataGrid anzeigen kann. Es erscheint ja normalerweise eine Auswahl aller Tables, bei der man sich erst eine aussuchen kann.

Alexander 3. Jun 2006 11:15

Re: Allgemeiner Datenbankzugriff unter .NET
 
Mal ein kleines dezentes *push* ;)

Jürgen Thomas 3. Sep 2006 15:58

Re: Allgemeiner Datenbankzugriff unter .NET
 
Zitat:

Zitat von Alexander
Doch wie kann ich die Daten in eine bestimmte Table des DataSets speichern?

Indem Du direkt die Tabelle angibst:
Delphi-Quellcode:
dataAdapter.Fill(Res, 'Kunden');
//  oder
dataAdapter.Fill(Res, tblKunden);
Zitat:

Zitat von Alexander
Außerdem wäre es interessant, wie ich dann genau die DataSEt.Table im DataGrid anzeigen kann.

Ebenfalls, indem Du die Table zur Anzeige festlegst, z.B.:
Delphi-Quellcode:
dataGrid1.SetDataBinding(myDataSet, 'Kunden');
Für den Fall, dass etwas noch nicht erledigt sein sollte. Gruß Jürgen

Alexander 3. Sep 2006 20:22

Re: Allgemeiner Datenbankzugriff unter .NET
 
Hallo,
vielen Dank für deine Antwort ! Doch das hat sich schon erledigt gehabt :).

Leider habe ich wohl vergessen, dies hier auch zu schreiben.

Elvis 3. Sep 2006 21:32

Re: Allgemeiner Datenbankzugriff unter .NET
 
Ich habe mir eigentlich nicht getraut dich hier zu deprimieren und den Weg zu zeigen, der in .Net 2.0 möglich wäre.
Dort kann man über die DbProviderFactories eine Factory nach Namen laden und sich natürlich alle registrierten Factories ausgeben lassen.

Selbst wenn dein DataProvider keine Factory besitzt lässt sich die mit ca. 50-60 Zeilen selbst schreiben (die Methoden überschreiben, die du bei deinem DBMS brauchst).
Du musst sie auch nicht im System regisrtieren, das .Net Konfigsystem schaut ja zuerst in deine App.config. Es reicht also dort deine Factory einzutragen.
Hier mal etwas pseudo code:
Delphi-Quellcode:
var factory := DbProviderFactories.GetFactory(factoryName);

using connection := factory.CreateConnection() do
using command   := connection.CreateCommand() do
begin
  with bldr := factory.CreateConnectionStringBuilder() do
  begin
     bldr['user id']    := deineUserId;
     bldr['password']   := deinPasswort;
     bldr['data source'] := deineDataSource;
     bldr['database']   := deineDatabase;
     connection.ConnectionString := bldr.ConnectionString;
  end;

  command.CommandText := 'SELECT X FROM Y WHERE Z = :Z';
  with prm := command.CreateParameter() do
  begin
    prm.ParameterName := 'Z';
    prm.Value := deinWert;
    command.Parameters.Add(prm);
  end;

  using da := factory.createDataAdpater() do
  begin
    da.SelectCommand := command;
    da.Fill(deineDatatable);
  end;
end;
Zusammen mit System.Transactions ist das ziemlich mächtig.

Jürgen Thomas 4. Sep 2006 08:31

Re: Allgemeiner Datenbankzugriff unter .NET
 
@Elvis,
ist das vielleicht auch eine vereinfachte Lösung anstelle von Bridge-Patterns, die Bernhard Geyer empfiehlt? Das würde meine eigenen Entscheidungen (siehe die Dir bekannten Diskussionen) beeinflussen. Jürgen

Elvis 4. Sep 2006 10:45

Re: Allgemeiner Datenbankzugriff unter .NET
 
Zitat:

Zitat von Jürgen Thomas
@Elvis,
ist das vielleicht auch eine vereinfachte Lösung anstelle von Bridge-Patterns, die Bernhard Geyer empfiehlt? Das würde meine eigenen Entscheidungen (siehe die Dir bekannten Diskussionen) beeinflussen. Jürgen

Naja ganz vorbeikommen wirst du daran nicht.
Aber ein guter Anfang wäre zum Beispiel wenn du die ProviderFactories in eine eigene Verpacken würdest und diese um einen "richtigen" CommandBuilder erweiterst.
Dem kannst du dann sagen was du woher abfragen willst und wie verknüpft und gefiltert wird.
Der Sql Server mit seinem ekelhaften TSQL ist inkompatibel zu so ziemlich allem was auf dem Name RDMS laut "hier" ruft.
Dummerweise haben die Jungs vom FirebirdProvider dieses widerliche @ und die widerliche Angewohnheit übernommen, dass man das widerliche @ immer dem Parameter voranstellen muss (also auch bei ParameterName :wall: ).
Ich fürchte MS plant mal wieder einen neuen "Standard" zu schaffen, sozusagen das was wir schon hatten und gut lief nur jetzt in hässlich und friemelig (wenn's einfach läuft ist's ja auch langweilig :zwinker: ).

Oki, zurück von meinem Depri über die Hirnies vom TSQL-Team...

Überlege dir wie der CommandBuilder aussehen muss und vor allem was er für dich können muss. (Selektierte Spalten?, Filter?, Joins?, Standardfunktionen á la SubStr, Trim,... übersetzen?)
Danach kannst du einfach ein Interface baust, dass praktisch die Member von DbProviderFactory hat, nur um ein CreateSqlBuilder erweitert.
Jetzt musst du im Endeffekt für jedes DBMS nur noch von deren DbFactory ableiten, die neue Methode implementieren und deiner Ableitung dein Interface verpassen.
Für den Rest reicht ein highjacking des bestehenden ADO.Net DbProvider-Systems:
Du baust dir also eine neue statische Klasse, die als Factory für dein Interface dient.
Die muss praktisch alles nur an die bestehende DbProviderFactory durchreichen.
Deine eigenen Factories brauchen natürlich einen eigenen InvariantName, da würde es schon reichen ein "JT." vor den bestehenden zu setzen.


Ooodeeer du schaust dir nHibernate an, was dir Bernhard und ich schon mehrfach empfohlen haben. :zwinker:
Erst wenn du nHibernate als untauglich ansehen solltest, würde ich Arbeit in eine eigne Implementierung investieren. Und auch da, wie oben beschreiben, möglichst viel von dem bestehenden System aus ADO.Net 2.0 wiederverwenden.

Jürgen Thomas 4. Sep 2006 11:53

Re: Allgemeiner Datenbankzugriff unter .NET
 
Ach Elvis,

bitte versteht mich doch nicht falsch: Ich hätte nichts dagegen, mich mit nHibernate zu befassen. Ich will/muss mich jetzt mit einem DBMS befassen, möchte mir aber die Wege offen halten, später weitere DBMS einzubeziehen. Deshalb sollte ich von Anfang an flexibel bleiben.

Dafür suche ich jetzt den geeigneten Einstieg - siehe [dp]Interbase, MS-SQL oder... Was ist für mich am besten?[/dp]. Jürgen

Elvis 4. Sep 2006 11:59

Re: Allgemeiner Datenbankzugriff unter .NET
 
nHibernate kann, je nach DB Design, auch für ein einzelnes DBMS praktischer sein als das ganze Command-Gerödel. ;-)

Alexander 4. Sep 2006 18:10

Re: Allgemeiner Datenbankzugriff unter .NET
 
Uff. Das sieht ja richtig schick aus :shock:.
Gerade auch der ConnectionStringBuilder ist ja heftig :).

Das würde mir echt viel Arbeit abnehmen...

Nachtrag: Den Builder kann man nicht zufällig irgendwie auch für .NET 1.1 bekommen, oder? Kann man nicht eine .NET 2.0 DLL erzeugen und diese irgendwie in Delphi.NET einbinden?

Elvis 4. Sep 2006 19:56

Re: Allgemeiner Datenbankzugriff unter .NET
 
Zitat:

Zitat von Alexander
Uff. Das sieht ja richtig schick aus :shock:.
Gerade auch der ConnectionStringBuilder ist ja heftig :).

Das würde mir echt viel Arbeit abnehmen...

Nachtrag: Den Builder kann man nicht zufällig irgendwie auch für .NET 1.1 bekommen, oder? Kann man nicht eine .NET 2.0 DLL erzeugen und diese irgendwie in Delphi.NET einbinden?

Ich hatte als du den Thread hier angefangen hast eine Lib geschreiben, die ähnliche Funktionalität hatte.
Im Endeffekt ging sie von der Connection aus und suchte sich alle passenden Klassen wie Command, CommandBuilder, DataAdapter und Konsorten.
Mal schauen ob ich sie irgendwo finde, war mir einfach irgendwann als dämliche Zeitverschwendung vorgekommen etwas neu zu erfinden, was ich schon seit einer halben Ewigkeit in .Net 2.0 benutze. ;)

Und ich ralle immer noch nicht was dich an 1.1 hält...

Alexander 4. Sep 2006 20:25

Re: Allgemeiner Datenbankzugriff unter .NET
 
Zitat:

Zitat von Elvis
Mal schauen ob ich sie irgendwo finde, war mir einfach irgendwann als dämliche Zeitverschwendung vorgekommen etwas neu zu erfinden, was ich schon seit einer halben Ewigkeit in .Net 2.0 benutze. ;)

Das wäre schön, wenn du den Quelltext wiederfinden könntest.

Zitat:

Und ich ralle immer noch nicht was dich an 1.1 hält...
So ganz verstehe ich das ja auch nicht :mrgreen:.
Es ist halt so, dass ich jetzt mit dem Programm unter .NET 1.1 angefangen habe und jetzt nicht so einfach zu .NET 2 umspringen möchte.
Außerdem bleibt halt die noch die Sache mit der Lizenz, aber das gehe ich demnächst mal an.
Die Testversion habe ich übrigens schon mal angetestet. Gefiel mir eigentlich recht gut :).
Das VS ist halt auch noch ein bissle Gewöhnungssache...


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:00 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz