Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   C# FirebirdClientFactory kann nicht genutzt werden (https://www.delphipraxis.net/77810-firebirdclientfactory-kann-nicht-genutzt-werden.html)

Jürgen Thomas 25. Sep 2006 09:51

Datenbank: Firebird • Version: 2.0 RC1 • Zugriff über: FB NET Provider 2.0.1 RC1

FirebirdClientFactory kann nicht genutzt werden
 
Hallo,

ich möchte mich auf einen DB-unabhängigen Zugriff vorbereiten und möchte dabei DbProviderFactory nutzen. Das klappt aber für FirebirdClientFactory nicht; die verschiedenen Fehlermeldungen helfen mir nicht weiter.

Der FB-NET-Provider ist in machine.config eingetragen (Dank an Carlos Guzmán Álvarez):
Code:
<configSections>
    <section name="firebirdsql.data.firebirdclient"
             type="System.Data.Common.DbProviderConfigurationHandler, System.Data,
             Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
...
<DbProviderFactories>
     <add name="FirebirdClient Data Provider"
                 invariant="FirebirdSql.Data.FirebirdClient"
                 description=".Net Framework Data Provider for Firebird"
                 type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory,
                      FirebirdSql.Data.FirebirdClient, Version=%Version%, Culture=%Culture%,
                      PublicKeyToken=%PublicKeyToken%" />
...
//  Subst:
%Version%        -> The version of the provider assembly you are using.
%Culture%        -> The culture of the provider assembly you are using.
%PublicKeyToken% -> The Public Key Token of the provider assembly you are using.
Ausgangspunkt ist folgender Versuch (nach @Elvis und anderen Hinweisen):
Code:
private System.Data.Common.DbProviderFactory dataFactory;
string s0 = System.Configuration.ConfigurationManager.AppSettings["provider"];
//  s0 enthält den korrekten Wert
dataFactory = System.Data.Common.DbProviderFactories.GetFactory(s0);
//  Fehlermeldung:
Exception System.TypeInitializationException was thrown in debuggee:
Der Typeninitialisierer für JThomas.DGW.Data.DbService hat eine Ausnahme verursacht.
Dieses Problem ist mir inzwischen klar, weil DbProviderFactory eine abstrakte Klasse ist. Also will ich gezielt FirebirdClientFactory einbinden, was mir aber nicht gelingt:

Versuch A
Code:
dataFactory = FirebirdSql.Data.FirebirdClient.FirebirdClientFactory;
Zitat:

Zitat von Fehlermeldung
FirebirdSql.Data.FirebirdClient.FirebirdClientFact ory ist ein Typ, das im gegebenen Kontext jedoch ungültig ist.

Versuch B mit gleicher Fehlermeldung:
Code:
dataFactory = FirebirdSql.Data.FirebirdClient.FirebirdClientFactory();
Versuch C
Code:
dataFactory = new FirebirdSql.Data.FirebirdClient.FirebirdClientFactory();
Zitat:

Zitat von Fehlermeldung
Der Typ FirebirdSql.Data.FirebirdClient.FirebirdClientFact ory hat keine definierten Konstruktoren.

Andererseits erfahre ich über Lutz Roeder's Reflector:
Zitat:

public static readonly FirebirdClientFactory Instance;
Declaring Type: FirebirdSql.Data.FirebirdClient.FirebirdClientFact ory
abgeleitet von: System.Data.Common.DbProviderFactory
Also müsste einer meiner Versuch funktionieren. Was verstehe ich hier noch (überhaupt) nicht?

Danke für erläuternde Hinweise! Jürgen

PS. Für dieses Problem hätte ich lieber ein NET- oder Firebird-Forum. Aber die DP wird so oft und gerne besucht (vor allem auch von DB-Fachleuten), dass ich hier die größte Erfolgsaussicht vermute.

Elvis 25. Sep 2006 10:14

Re: FirebirdClientFactory kann nicht genutzt werden
 
Du musst es nicht in die Maschine.config packen. Dazu braucht man Adminrechte, außerdem kann es so "Kollateralschäden" geben. ;)

Viel "cooler" ist es einfach die app.config zu nehmen:
XML-Code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="applicationSettings"
                  type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="ConsoleApplication12.Properties.Settings"
               type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
               requirePermission="false" />
    </sectionGroup>
    <section name="system.data"
             type="System.Data.Common.DbProviderFactoriesConfigurationHandler,
                   System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>
  <applicationSettings>
    <ConsoleApplication12.Properties.Settings>
      <setting name="DefaultProviderName"
               serializeAs="String">
        <value>FirebirdSql.Data.FirebirdClient</value>
      </setting>
    </ConsoleApplication12.Properties.Settings>
  </applicationSettings>
  <system.data>
    <DbProviderFactories>
      <add name="Firebird Data Provider for .NET"
           invariant="FirebirdSql.Data.FirebirdClient"
           description="Firebird Data Provider for .NET"
           type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory,
                 FirebirdSql.Data.FirebirdClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c" />
    </DbProviderFactories>
  </system.data>
</configuration>
Ich habe meinem Project ein Setting namens DefaultProviderName verpasst. (Sieht man auch in der app.config :zwinker: )
Deshalb kann ich jetzt das Ganze einfach so machen:
Code:
DbProviderFactory factory = DbProviderFactories.GetFactory(Settings.Default.DefaultProviderName);
using (IDbConnection connection = factory.CreateConnection())
{
    Console.WriteLine(connection);
}
Da der XML-Inhalt da oben schon sehr hässlich ist, hier nochmal auf deutsch:

Die ProviderFactory schaut in den Configs nach einer Section namens "system.data".
Diese Section besitzt einen Handler, der weiß wie man Infos daraus ziehen kann (System.Data.Common.DbProviderFactoriesConfigurati onHandler).
In der Config datei werden oben die Sections deklariert, die man benutzen möchte und welche Klasse dafür zuständig ist.
Wie du sehen kannst habe ich dort "system.data" deklariert.
Im Hauptteil der Config-Datei habe ich jetzt unter "system.data" einfach einen weiteren Provider hinzugefügt.
Wie das geht muss man sich nichtmal merken, einfach aus der Maschine.config den system.data-Teil herauskopieren und einen der dortigen Provider mit dem gewünschten ersetzen.
Das was unter invariant steht ist der String, mit dem man den Provider im Programm abfragen kann. Ich nahm hier den Namespace der ADO.Net-Komponenten von FB, aber "Hallo.Du.Da" wäre ebenfalls möglich gewesen...)

Solche Späße in die app.config zu packen hat ein paar Vorteile:
  • Du brauchst keine Adminrechte
  • Du zwingst anderen Apps keine neuere/ältere Version des FbProviders auf
  • andere Apps können dir keine andere Version aufdrängen
  • Du musst den Provider nicht im GAC haben (xcopy Deployment :zwinker: )

edit2: Ups, hatte die system.data section in die appsetings sectionGroup kopiert... :oops:

Jürgen Thomas 25. Sep 2006 11:18

Re: FirebirdClientFactory kann nicht genutzt werden
 
Danke, Elvis, für die schnelle Antwort. Hier zunächst ebenso schnelle Anmerkungen.

Mir fehlt (unter vielem anderen) ein Tutorial o.ä., was und wie man praktisch mit den applicationSettings umgeht. Bei meinem ersten Versuch (siehe mein erster C-Quellcode) hatte ich festgestellt, dass der FB-Provider überhaupt nicht bekannt war. Als ich die Original-NET-Provider in machine.config fand, habe ich das (mit Hinweis durch Carlos) für FB erweitert.

Im Moment stecke ich in anderen Überlegungen fest (dort komme ich allerdings voran); deshalb werde ich Deine Hinweise erst etwas später untersuchen und benutzen.

Unklar bleibt mir aber auf jeden Fall: Was ist mit FirebirdClientFactory und den von mir festgestellten Problemen (Versuche A, B, C)?

Danke für Ergänzungen! Jürgen

Elvis 25. Sep 2006 11:51

Re: FirebirdClientFactory kann nicht genutzt werden
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Jürgen Thomas
Also will ich gezielt FirebirdClientFactory einbinden, was mir aber nicht gelingt:
Versuch A
Code:
dataFactory = FirebirdSql.Data.FirebirdClient.FirebirdClientFactory;
Zitat:

Zitat von Fehlermeldung
FirebirdSql.Data.FirebirdClient.FirebirdClientFact ory ist ein Typ, das im gegebenen Kontext jedoch ungültig ist.

Versuch B mit gleicher Fehlermeldung:
Code:
dataFactory = FirebirdSql.Data.FirebirdClient.FirebirdClientFactory();
Versuch C
Code:
dataFactory = new FirebirdSql.Data.FirebirdClient.FirebirdClientFactory();
Zitat:

Zitat von Fehlermeldung
Der Typ FirebirdSql.Data.FirebirdClient.FirebirdClientFact ory hat keine definierten Konstruktoren.


FirebirdClientFactory hat keinen öffentlichen Konstruktor!
Du kannst also höchstens FirebirdClientFactory.instance benutzen. Oder du gehst den Weg über die ProviderFactory, die macht nichts anderes.
Versuch A kann nicht gehen, da du einen Typen nicht einfach so in eine Variable stecken kannst (Kontext ungültig :zwinker: )
Versuch B kann nicht gehen, da ein Typ keine Funktion ist, selbst ein "new" (Versuch C) davor kann nicht gehen, da der Contructor private ist.
FirebirdClientFactory ist nunmal ein Singleton, auch wenn das hier IMHO Blödsinn ist.

Ich glaube der von mir gezeigte Weg ist der schniekeste, denn:
  • Du hast keine fixe Referenz auf den FbProvider in deinem Code
  • Das Settings system im VS05 macht solche Dinge sehr easy, da du eine passend typisierte Eigenschaft für jedes Settign bekommst.
Um Settings nutzen zu können musst du die Projektoptionen öffnen, dort seitlich auf den Reiter "Settings" gehen.
Wenn du noch keine Settings hast, findest du einen Link, der sie anlegen wird.
Nun kannst du einfach einen neuen Eintrag anlegen und ihm einen Default wert geben. (im Screenie siehst du den, den ich für den Bleistift code genommen habe)
Jetzt solltest du das hier machen können:
Code:
DbProviderFactory factory = DbProviderFactories.GetFactory(Settings.Default.DefaultProviderName);
using (IDbConnection connection = factory.CreateConnection())
{
    Console.WriteLine(connection);
}
btw: Den Teil des type-Attributes in der Provider-section, kannst du direkt aus dem Reflector kopieren. Dadurch kannst du dir sicher sein, dass Version, Culture und Public key token richtig sind. :)

Jürgen Thomas 26. Sep 2006 11:07

Re: FirebirdClientFactory kann nicht genutzt werden
 
Hallo Elvis,

jetzt versuche ich den ganzen Vormittag, Deine Anleitung zu verstehen und umzusetzen, aber ich bin offensichtlich zu dämlich.

Alles, was Du mir zum Bereich <system.data> und der Einbindung des FirebirdClient geschrieben hast, ist klar; es handelt sich ja in der Tat 'nur' um das Verschieben von machine.config nach app.config.

Beim Lesen der Werte aus der config-Datei habe ich aber keinerlei Erfolg:
  • Mit #D kann ich in den Projektoptionen keine Settings direkt einstellen.
  • Du registrierst eine sectionGroup "applicationSettings", die NET-Doku verwendet immer "appSettings". Ist das nur ein Unterschied in der Schreibweise oder ein wirklicher Unterschied?
  • In Deinem C#-Quellcode schreibst Du:
    Code:
    ... Settings.Default.DefaultProviderName ...
    'Settings' finde ich in der NET-Doku nur als AppSettingsSection-Eigenschaft, auf die z.B. über ConfigurationManager.AppSettings zugegriffen werden kann; 'Default' finde ich überhaupt nicht in diesem Zusammenhang.
  • Diese Settings werden als key-value-Collection angegeben, während Dein DefaultProviderName über name-value arbeitet; auch das kann ich nicht miteinander verbinden.
Kannst Du mir vielleicht noch einen Code-Schnipsel nennen mit vollständigen Angaben von Namespace, Klassen und Eigenschaften, mit denen ich zunächst sicherstellen kann, dass ich den richtigen String erhalte?

Nachtrag zur FirebirdClientFactory: Welche Information habe ich überlesen, dass dies eine Singleton-Klasse ist? Hätte mich vor allem die Eigenschaft 'Instance' darauf hinweisen sollen?

Hier teile ich Deine Einschätzung, dass dies als Singleton eher Quatsch ist: Für mehrere DBs benutzt man doch wohl auch mehrere Instanzen von DbProviderFactory, oder?

Danke für weitere Hinweise und Erläuterungen! Jürgen

Elvis 26. Sep 2006 11:24

Re: FirebirdClientFactory kann nicht genutzt werden
 
Zitat:

Zitat von Jürgen Thomas
Alles, was Du mir zum Bereich <system.data> und der Einbindung des FirebirdClient geschrieben hast, ist klar; es handelt sich ja in der Tat 'nur' um das Verschieben von machine.config nach app.config.

Genau! :)
  • Zitat:

    Mit #D kann ich in den Projektoptionen keine Settings direkt einstellen.
    Schaue dir mal C# Express an. Du kannst dort die gleichen Projektdateien wie in #D öffnen, aber du hast ebenfalls solche netten Spielereien wie das autom. Generieren einer Wrapperklasse für deine Settings.
  • Zitat:

    Du registrierst eine sectionGroup "applicationSettings", die NET-Doku verwendet immer "appSettings". Ist das nur ein Unterschied in der Schreibweise oder ein wirklicher Unterschied?
    Ist vom VS so generiert, aber ich kann die Section in meiner App.config auch als Hallo.Du.Da deklarieren. Wichtig ist nur, dass jeder Section/SectionGroup der richtige Handlerzugewisen wird.
  • Zitat:

    In Deinem C#-Quellcode schreibst Du:
    Code:
    ... Settings.Default.DefaultProviderName ...
    'Settings' finde ich in der NET-Doku nur als AppSettingsSection-Eigenschaft, auf die z.B. über ConfigurationManager.AppSettings zugegriffen werden kann; 'Default' finde ich überhaupt nicht in diesem Zusammenhang.
    Diese Settings werden als key-value-Collection angegeben, während Dein DefaultProviderName über name-value arbeitet; auch das kann ich nicht miteinander verbinden.
    Wie gesagt, das VS generiert automatisch eine Wrapperklasse für die definierten Settings.

Zitat:

Kannst Du mir vielleicht noch einen Code-Schnipsel nennen mit vollständigen Angaben von Namespace, Klassen und Eigenschaften, mit denen ich zunächst sicherstellen kann, dass ich den richtigen String erhalte?
Code:
// der invariantName den du dem Provider verpasst hast
// ich nahm den hier:
DbProviderFactory factory = DbProviderFactories.GetFactory("FirebirdSql.Data.FirebirdClient");
using (IDbConnection connection = factory.CreateConnection())
{
    Console.WriteLine(connection);
}
Zitat:

Nachtrag zur FirebirdClientFactory: Welche Information habe ich überlesen, dass dies eine Singleton-Klasse ist? Hätte mich vor allem die Eigenschaft 'Instance' darauf hinweisen sollen?
Genau. Ob du es Singleton nennst oder wie auch immer. Ein privater Konstruktor und ein statisches Feld, das die einzige Instanz hält, sieht für mich sehr danach aus. ;)
Zitat:

Hier teile ich Deine Einschätzung, dass dies als Singleton eher Quatsch ist: Für mehrere DBs benutzt man doch wohl auch mehrere Instanzen von DbProviderFactory, oder?
Die Klasse hat keine Felder pro Instanz. Ein Singleton zu haben hat hier IMHO keinen wirklichen Vorteil. Selbst der gesparte Speicherplatz ist komplett vernachlässigbar, aber die statische Instanz macht das Laden und Entladen der Assembly durch eine AppDomain teurer als es sein müsste.
Diese Macke haben alle DbProviderFactories, vllt werden sie irgendwo über Referenzen verglichen. Keine Ahnung, wirklich... :gruebel:

Jürgen Thomas 28. Sep 2006 10:25

Re: FirebirdClientFactory kann nicht genutzt werden
 
Hallo Elvis,

nach vielem langem Probieren bin ich jetzt endlich fündig geworden und kann auf meine FB-DB zugreifen. Mein Fehler, warum ich den FirebirdClient nicht aktivieren konnte, war ganz banal:
XML-Code:
<system.data>
   <DbProviderFactories>
      <add name="FirebirdClient NET-Provider"
          invariant="FirebirdSql.Data.FirebirdClient"
          description="FirebirdClient Data Provider for .NET"
          type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory,
                         FirebirdSql.Data.FirebirdClient,
                         Version=2.0.1.0,  
                         Culture=neutral, PublicKeyToken=3750abcc3150b00c" />
   </DbProviderFactories>
</system.data>
Speicherung in GAC und Appl-Verzeichnis, PublicKeyToken und app.config habe ich mehrfach in verschiedenen Situationen geprüft; aber auf den Gedanken, dass die Versionsnummer falsch sein könnte, bin ich nicht gekommen...

Meine Versuche umfassten: Buch aus der Stadtbibliothek geholt, genau und gezielt gelesen, NET-Doku untersucht, Visual C# Express von MS geholt und installiert (damit ich den Settings-Manager nutzen konnte), Roeder's Reflector mit Details untersucht - und schließlich Fusion_Log_View.exe genutzt.

So kann Zeit verloren gehen; aber dabei lernt man natürlich zusätzlich.

Jedenfalls vielen Dank für Deine Hilfe! Jürgen

Elvis 28. Sep 2006 10:29

Re: FirebirdClientFactory kann nicht genutzt werden
 
Zitat:

Zitat von Jürgen Thomas
nach vielem langem Probieren bin ich jetzt endlich fündig geworden und kann auf meine FB-DB zugreifen. Mein Fehler, warum ich den FirebirdClient nicht aktivieren konnte, war ganz banal:
<snip>
Speicherung in GAC und Appl-Verzeichnis, PublicKeyToken und app.config habe ich mehrfach in verschiedenen Situationen geprüft; aber auf den Gedanken, dass die Versionsnummer falsch sein könnte, bin ich nicht gekommen...

Warum nicht einfach fragen? Hätte dir einiges an Zeit gespart. :zwinker:


Sorry, aber das kann ich mir jetzt nicht verkneifen:
Zitat:

Zitat von Elvis
btw: Den Teil des type-Attributes in der Provider-section, kannst du direkt aus dem Reflector kopieren. Dadurch kannst du dir sicher sein, dass Version, Culture und Public key token richtig sind. :)

Genau da habe ich nämlcih eines deiner Probleme vermutet. ;)

kiar 1. Okt 2006 13:24

Re: FirebirdClientFactory kann nicht genutzt werden
 
moin Elvis :mrgreen:

wollte das mit den DbProviderFactory- Klasse mal probieren. leider kennt er diese Klasse nicht.
jetzt bin ich verzweifelt auf der Suche nach der .dll .

gib mir mal einen Tip :zwinker:

danke Raik

// edit Leichte Schläge auf den Hinterkopf erhöhen das Denkvermögen :wall:

System.Data.Common

Elvis 1. Okt 2006 13:31

Re: FirebirdClientFactory kann nicht genutzt werden
 
Zitat:

Zitat von kiar
hläge auf den Hinterkopf erhöhen das Denkvermögen :wall:
System.Data.Common

Da kann und will ich dir nicht widersprechen, Raik. :mrgreen:
Reflector -> F3 -> DbPoviderFactory -> Erleuchtung. ;)

btw: Wollten wir uns oben in der Altmark nichtmal auf ein Bier treffen? :zwinker:


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:16 Uhr.
Seite 1 von 2  1 2      

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