Schemas in ADO.NET 2.0
In diesem Artikel erfahren Sie, welche erweiterte Unterstützung ADO.NET 2.0 für den Zugriff auf Metadaten in Datenquellen bietet.
Downloaden Sie das zugehörige Codebeispiel SchemasSample.exe.
Auf dieser Seite
Die neue gemeinsame Metadaten-API im Detail
In welchen Fällen sind Metadaten von Bedeutung?
Welche Metadaten können abgerufen werden?
Die Auflistung "Restrictions"
Die Auflistung "DataSourceInformation"
Anpassen und Erweitern von Metadaten
Anpassungen durch den Benutzer
Schlussfolgerung: Abschließende Anmerkungen zur Metadatenunterstützung
Die neue gemeinsame Metadaten-API im Detail
Im vorhergehenden Artikel habe ich darauf hingewiesen, dass im Server-Explorer von Visual Studio 2005 ein Dialogfeld mit einer Liste der .NET-Datenprovider (und nicht wie bisher der OLE DB-Provider) zur Eingabe von Verbindungsinformationen verwendet wird. Wenn Sie eine Verbindungszeichenfolge angeben und eine Datenverbindung hinzufügen, wird für jede Datenverbindung zusätzlich eine Informationsstruktur mit allen Datenbankobjekten angezeigt (wie beispielsweise Tabellen, Sichten und gespeicherte Prozeduren), auf die über diese Verbindung direkt zugegriffen werden kann. Woher stammen diese Informationen? Wenn es sich um statische Informationen handelt, die in Visual Studio nur für bestimmte Datenprovider ausgegeben werden, würde dies bedeuten, dass für selbst entwickelte Datenprovider und für Datenprovider von Drittanbietern keine Informationen verfügbar sind und ein leerer Knoten angezeigt wird. Das ist in Visual Studio 2005 nicht der Fall. Diese nützlichen Informationen werden mittels der neuen Schema-API in ADO.NET 2.0 zur Verfügung gestellt. Das folgende Codebeispiel zeigt, wie Sie eine Liste der Tabellen einer Datenbank unter Verwendung der neuen APIs abrufen können.
// uses a ADO.NET 2.0 named connection string in config file
// uses ADO.NET 2.0 ProviderFactory and base classes
// see previous article
public static void GetListOfTables(string connectstring_name)
{
ConnectionStringSettings s =
ConfigurationSettings.ConnectionStrings[connectstring_name];
DbProviderFactory f = DbProviderFactories.GetFactory(s.ProviderName);
using (DbConnection conn = f.CreateConnection())
{
conn.ConnectionString = s.ConnectionString;
conn.Open();
DataTable t = conn.GetSchema("Tables");
t.WriteXml("tables.xml");
}
}
In welchen Fällen sind Metadaten von Bedeutung?
Metadaten sind Bestandteil jeder API für den Datenzugriff. Sie werden hauptsächlich von Tools wie Visual Studio oder Codegeneratoren wie DeKlarit verwendet. Dennoch gibt es andere Situationen, in denen Zugriffe auf Metadaten hilfreich sein können. Entwickler von Anwendungspaketen können Endbenutzern die Möglichkeit geben, eine Anwendung durch Hinzufügen neuer Tabellen oder neuer Tabellenspalten anzupassen. Wenn Endbenutzer das Datenbankschema auf diese Weise ändern, können die neuen Benutzertabellen mit einem universellen Abfrage- und Bearbeitungstool und unter Verwendung der Metadaten so in Verwaltungs-, Sicherungs- und andere Anwendungsfunktionen integriert werden, als handele es sich um fest integrierte Tabellen der ursprünglichen Anwendung. Programmierer können auf Metadaten zugreifen, um von System.Data.Common.DbCommandBuilder abgeleitete, benutzerdefinierte Klassen zu schreiben und Einfüge-, Aktualisierungs- und Löschbefehle zur Verwendung mit DataSet zu erstellen. Ersteller von Anwendungen für mehrere Datenbanken (d. h. Anwendungen, die mit der jeweils vom Benutzer ausgewählten Datenbank ausgeführt werden) können mithilfe von Metadaten eine möglichst einheitliche Codebasis bewahren und den Datenzugriffscode dort optimieren, wo dies erforderlich ist.
Klar ersichtlich sind die Vorteile einer generischen Metadaten-API gegenüber datenbankspezifischen APIs, die nur Zugriffe auf die jeweils unterstützte Datenbank erlauben. Die Codebasis entwickelter Tools bleibt auf diese Weise überschaubar und lässt sich leichter verwalten. Eine derartige API muss allerdings sehr flexibel sein, da beim Schreiben einer generischen API zum Offenlegen von Metadaten vier wesentliche Schwierigkeiten zu bewältigen sind.
-
Die Metadatenauflistungen und -informationen verschiedener Datenbanken unterscheiden sich voneinander. Benutzer von SQL Server beispielsweise benötigen eine Auflistung der Verbindungsserver (Linked Servers), während Benutzer von Oracle etwa Informationen zu Oracle-Sequenzen (Oracle Sequences) abrufen möchten.
-
Die zugrunde liegenden Systemtabellen, in denen allgemeine Datenbank-Metadaten gespeichert werden, unterscheiden sich nicht nur in unterschiedlichen Datenbankprodukten, sondern auch in verschiedenen Versionen derselben Datenbank. So legt beispielsweise SQL Server 2005 seine Metadaten in neuen Tabellen unter einem sys-Schema offen (zum Beispiel sys.tables), während frühere Versionen von SQL Server Metadatentabellen wie sysobjects zum Speichern derselben Daten verwenden.
-
Unterschiedliche Anwendungen können unterschiedliche Sichten von Metadaten enthalten. So beklagen sich z. B. viele Programmierer über die langen Tabellenlisten in Oracle-Datenbanken, da die meisten Metadaten-APIs Systemtabellen mit Benutzertabellen mischen. Sie würden eine kurze Liste bevorzugen, die nur die von ihnen definierten Tabellen enthält.
-
Ob Metadaten überhaupt unterstützt und wie viele Metadaten bereitgestellt werden, sollte dem Entwickler des Datenproviders überlassen bleiben.
Die meisten Datenbank-APIs stellen einen Standard-Metadatensatz bereit, den alle Provider unterstützen müssen, und erlauben Providerentwicklern, neue Metadatentabellen hinzuzufügen. Diesem Ansatz folgt auch der ANSI SQL-Standard. Im Teil 11, "Schema Schemata", (Abschnitte INFORMATION_SCHEMA und DEFINITION_SCHEMA) des Standards wird dieses Thema genauer behandelt. Im "ANSI SQL INFORMATION_SCHEMA" wird ein Standardsatz von Metadatensichten definiert, die von einer kompatiblen Datenbank unterstützt werden müssen. Aber auch in der Spezifikation müssen die zuvor genannten Punkte in irgendeiner Form beschrieben werden. Folgende Richtlinie wird in der Spezifikation formuliert:
"In einer Implementierung können INFORMATION_SCHEMA zusätzliche Tabellen hinzugefügt oder die vordefinierten INFORMATION_SCHEMA-Tabellen um zusätzliche Spalten ergänzt werden."
Ein Beispiel für eine Datenzugriffs-API, die vom Konzept her mit dem ANSI SQL-Standard übereinstimmt, ist eine von OLE DB definierte Reihe von Metadaten mit der Bezeichnung "Schemarowsets". Diese Metadaten basieren auf INFORMATION_SCHEMA und wurden um OLE DB-spezifische Spalten ergänzt. ADO.NET 2.0 bietet einen noch leistungsfähigeren und flexibleren Mechanismus zum Offenlegen von Metadaten.
Welche Metadaten können abgerufen werden?
Mit ADO.NET 2.0 kann ein Providerentwickler fünf verschiedene Metadatentypen offen legen. Diese primären "Metaauflistungen" oder "Kategorien" werden in der Klasse System.Data.Common.DbMetaDataCollectionNames aufgelistet.
-
MetaDataCollections - Eine Liste der verfügbaren Metadatenauflistungen
-
Restrictions - Ein Array mit Bezeichnern für jede Metadatenauflistung, mit denen der Umfang der angeforderten Schemainformationen eingeschränkt werden kann
-
DataSourceInformation - Informationen zur Datenbankinstanz, auf die der Datenprovider verweist
-
DataTypes - Ein Satz mit Informationen zu jedem Datentyp, den die Datenbank unterstützt
-
ReservedWords - Reservierte Wörter für die Abfragesprache der Datenbank. Die Abfragesprache ist meist ein SQL-Dialekt.
MetaDataCollections ist die Bezeichnung für die INFORMATION_SCHEMA-Auflistungen, wie z. B. Tables, Columns oder PrimaryKeys. Wenn Sie jedoch DbConnection.GetSchema verwenden, werden diese Metadatenkategorien wie Metadaten behandelt. In Hinblick auf den Code bedeutet dies, dass die Auflistungen wie gewöhnliche Metadaten abgerufen werden können.
// gets information about database Views
Table t1 = conn.GetSchema("Views");
// gets information about collections exposed by this provider
// this includes the five "meta-collections" described above
Table t2 = conn.GetSchema(DbMetaDataCollectionNames.MetaDataCollections);
// gets information about the Restrictions meta-collection
Table t3 = conn.GetSchema(DbMetaDataCollectionNames.Restrictions);
// No argument overload is same as asking for MetaDataCollections
Table t4 = conn.GetSchema();
Zwei der fünf Metaauflistungen bedürfen einer ausführlicheren Erläuterung.
Die Auflistung "Restrictions"
Mit der
Restrictions-Auflistung kann die Menge der zurückgegebenen Metadaten begrenzt werden. Falls Sie mit OLE DB oder ADO vertraut sind: Der Begriff "Restriction" (Einschränkung) hat in diesen APIs dieselbe Bedeutung. Das folgende Beispiel veranschaulicht die Funktionsweise der Restrictions-Auflistung anhand der MetaDataCollection Columns, die die Spaltennamen aller Tabellen enthält. Mit dieser Auflistung lassen alle Spalten aller Tabellen abrufen. Der Satz der angeforderten Spalten kann jedoch auf bestimmte Werte für Datenbankname, Besitzer/Schema oder Tabelle eingeschränkt werden. Jede Metadatenauflistung verfügt über verschiedene mögliche Einschränkungen, für die jeweils ein Standardwert festgelegt sein kann. Im Folgenden finden Sie eine XML-Darstellung der Einschränkungen für die Columns-Metadaten:
Codebeispiel 1. Einschränkungen für die Auflistung "Columns" (XML-Format)
<Restrictions> <CollectionName>Columns</CollectionName> <RestrictionName>Catalog</RestrictionName> <RestrictionDefault>table_catalog</RestrictionDefault> <RestrictionNumber>1</RestrictionNumber> </Restrictions> <Restrictions> <CollectionName>Columns</CollectionName> <RestrictionName>Owner</RestrictionName> <RestrictionDefault>table_schema</RestrictionDefault> <RestrictionNumber>2</RestrictionNumber> </Restrictions> <Restrictions> <CollectionName>Columns</CollectionName> <RestrictionName>Table</RestrictionName> <RestrictionDefault>table_name</RestrictionDefault> <RestrictionNumber>3</RestrictionNumber> </Restrictions> <Restrictions> <CollectionName>Columns</CollectionName> <RestrictionName>Column</RestrictionName> <RestrictionDefault>column_name</RestrictionDefault> <RestrictionNumber>4</RestrictionNumber> </Restrictions>
Einschränkungen werden durch eine Überladung von DbConnection.GetSchema angegeben. Die Einschränkungen werden als Array dargestellt. Sie können ein Array angeben, das alle Restrictions-Auflistungen oder nur eine Untermenge umfasst, da RestrictionNumbers i. d. R. mit der geringsten Einschränkung beginnt und mit der stärksten Einschränkung endet. Verwenden Sie für Einschränkungswerte, die Sie auslassen möchten, einen Nullwert (nicht den Datenbankwert NULL, sondern einen Nullwert in .NET bzw. Nothing in Visual Basic .NET). Beispiel:
// restriction string array
string[] res = new string[4];
// all columns, all tables owned by dbo
res[1] = "dbo";
DataTable t1 = conn.GetSchema("Columns", res);
// clear collection
for (int i = 0; i < 4; i++) res[i] = null;
// all columns, all tables named "authors", any owner/schema
res[2] = "authors";
DataTable t2 = conn.GetSchema("Columns", res);
// clear collection
for (int i = 0; i < 4; i++) res[i] = null;
// columns named au_lname
// all tables named "authors", any owner/schema
res[2] = "authors"; res[3] = "au_lname";
DataTable t3 = conn.GetSchema("Columns", res);
// clear collection
for (int i = 0; i < 4; i++) res[i] = null;
// columns named au_lname
// any tables, any owner/schema
res[3] = "name";
DataTable t4 = conn.GetSchema("Columns", res);
Sie müssen nicht das gesamte Einschränkungsarray angeben. Im vorigen Beispiel, in dem nur Tabellenspalten mit dem Besitzer dbo angezeigt werden sollen, müssen lediglich zwei und nicht alle vier Arrayelemente angegeben werden. Beachten Sie auch, dass die Angabe einer leeren Zeichenfolge als Einschränkung ein anderes Ergebnis liefert als die Angabe eines Nullwertes (Nothing in Visual Basic .NET). Sie müssen die Einschränkungen nicht protokollieren. Sie können wie jede andere Auflistung jederzeit abgefragt werden. Die Restrictions-Auflistung selbst lässt keine Einschränkungen zu. Da die Informationen jedoch in eine DataTable eingelesen werden, können Sie mit einer DataView ein ähnliches Ergebnis erzielen. Hierzu folgendes Beispiel:
DataTable tv = conn.GetSchema(DbMetaDataCollectionNames.Restrictions);
DataView v = tv.DefaultView;
// show restrictions on the "Columns" collection, sorted by number
v.RowFilter = "CollectionName = 'Columns'";
v.Sort = "RestrictionNumber";
for (int i = 0; i < tv.Count; i++)
Console.WriteLine("{0} (default){1}",
tv.Rows[i]["RestrictionName"],
tv.Rows[i]["RestrictionDefault"]);
Die Auflistung "DataSourceInformation"
Die DataSourceInformation-Auflistung enthält Informationen zur aktuellen Datenbankinstanz (Datenquelle) für Abfrage-Generatoren. Der Provider kann beliebige Inhalte in dieser Auflistung bereitstellen. Bei Verwendung der Microsoft-Provider (SqlClient, OracleClient, OleDb, Odbc) sind folgende Informationen in dieser Auflistung verfügbar:
Tabelle 1. Inhalte der Auflistung "DataSourceInformation" bei Microsoft-Providern
|
Wert |
Format/Bedeutung |
|
CompositeIdentifierSeparatorPattern |
Trennzeichen für mehrteilige Namen (z. B. der Punkt in pubs.dbo.authors) |
|
DataSourceProductName |
Datenbankname |
|
DataSourceProductVersion |
Datenbankversion. Beachten Sie, dass es sich hierbei um die Version der Datenbankinstanz handelt, auf die DbConnection aktuell zugreift. |
|
DataSourceProductVersionNormalized |
|
|
GroupByBehavior |
Enumeration, System.Data.Common.GroupByBehavior |
|
IdentifierPattern |
Zeichenfolge mit einem regulären Ausdruck |
|
IdentifierCase |
Enumeration, System.Data.Common.IdentifierCase |
|
OrderByColumnsInSelect |
Boole'scher Wert. Gibt an, ob ORDER BY in der Standardeinstellung auf die Spalten in einer SELECT-Anweisung angewendet wird. |
|
ParameterMarkerFormat |
Gibt an, ob Parametermarker mit einem Sonderzeichen beginnen (z. B. "@" für T-SQL). |
|
ParameterMarkerPattern |
Zeichenfolge mit einem regulären Ausdruck für das Erstellen von Parametern |
|
ParameterNameMaxLength |
Maximale Länge eines Parameters |
|
ParameterNamePattern |
Zeichenfolge mit einem regulären Ausdruck für das Erstellen von Parametern |
|
QuotedIdentifierPattern |
Zeichenfolge mit einem regulären Ausdruck für die Angabe von Bezeichnern |
|
QuotedIdentifierCase |
Enumeration, System.Data.Common.IdentifierCase |
|
StatementSeparatorPattern |
Zeichenfolge mit einem regulären Ausdruck |
|
StringLiteralPattern |
Zeichenfolge mit einem regulären Ausdruck |
|
SupportedJoinOperators |
Enumeration, System.Data.Common.SupportedJoinOperators |
Diese Informationen sollten ausreichen, um SQL-Abfragen für einen bestimmten Datenbankdialekt zu erstellen. Abschließend wäre es interessant zu wissen, ob der Provider in parametrisierten Abfragen benannte Parameter oder Positionsparameter verwendet. In meinem letzten Artikel über das Schreiben von Provider-unabhängigem Code finden Sie Informationen zu benannten Parametern und Positionsparametern. Beide Varianten erlauben das Schreiben parametrisierter Befehle.
Anpassen und Erweitern von Metadaten
Nachdem in den vorhergehenden Abschnitten die verfügbaren Basismetadaten und die Verwendung von DbConnection.GetSchema() beschrieben wurden, wird im Folgenden erläutert, wie Providerentwickler Metadaten mithilfe eines einfachen deklarativen Formats anpassen und wie Programmierer sich dieses Format zunutze machen können. Dies führt zurück zu den bereits am Anfang des Artikels erwähnten Schwierigkeiten: Wie lassen sich Metadaten zur Verfügung stellen, die von der Datenbankversion unabhängig sind, und wie können verschiedenen Kunden unterschiedliche Sichten derselben Metadaten bereitgestellt werden?
Zunächst ist festzuhalten, dass jede Unterstützung von Metadaten optional ist. Provider müssen DbConnection.GetSchema nicht unterstützen und können für diese Methode die Ausnahme NotSupportedException zurückgeben. Darüber hinaus muss lediglich die MetaDataCollections-Kategorie bereitgestellt werden, wenn der Providerentwickler eine Unterstützung von DbConnection.GetSchema implementiert. Ob eine der anderen vier Informationskategorien unterstützt wird, ist vollständig abhängig vom Provider.
Jeder Provider kann zudem unterschiedliche Informationen für dieselbe Metadatenauflistung offen legen. Die Struktur der Tables-Auflistung ist beispielsweise ganz dem Providerentwickler überlassen. Der SqlClient-Provider legt in der Tables-Auflistung beispielsweise vier Informationselemente offen: table_catalog, table_schema, table_name und table_type. Der OracleClient-Provider hingegen legt nur drei Informationselemente offen (OWNER, TABLE_NAME und TYPE), da Oracle-Datenbanken nur einen Katalog enthalten können. Die Anzahl der Einschränkungen und Einschränkungs-Elemente kann sich von Provider zu Provider unterscheiden. Im Falle des Tables-Beispiels unterstützt der SqlClient-Provider vier Einschränkungen und der OracleClient-Provider nur zwei. Auch müssen die Einschränkungen keine bestimmte Reihenfolge aufweisen. Es gibt also keine vorgeschriebene Metadatenstruktur, Metadatenanzahl oder Metadatenreihenfolge wie bei den OLE DB- und ODBC-APIs. Der Provider kann frei wählen, welche Metadaten offen gelegt werden. Wenn in einer bestimmten Anwendung (z. B. in Visual Studio) jedoch konsistente Metadaten für alle verwendeten .NET-Datenprovider verfügbar sein sollen, kann zu diesem Zweck das Standardverhalten des Providers überschrieben werden. Dies wird im Abschnitt Anpassungen durch den Benutzer genauer beschrieben.
Providerentwickler können im Provider eine fest codierte Metadatenlogik implementieren. Dabei setzt jeder Providerentwickler möglicherweise einen anderen internen Algorithmus ein, um ähnliche Metadaten abzurufen. Dies entspricht der bisherigen Vorgehensweise, die beispielsweise bei der Implementierung der ISchemaRowset-Methode von OLE DB zur Anwendung kam. In ADO.NET 2.0 stehen den Providerentwicklern jedoch einige Basisklassen im System.Data.ProviderBase-Namespace zur Verfügung. Die vier erhältlichen Microsoft-Provider verwenden diese Basisklassen und implementieren daher die Schemas alle auf ähnliche Weise. Auf diese Implementierung wird auch in den folgenden Ausführungen zurückgegriffen, in der Hoffnung, dass führende Providerentwickler wie DataDirect Technologies u. a. ebenfalls diesem Verfahren folgen werden.
Die Basisklasse für das Offenlegen von Metadaten ist DbMetaDataFactory. Provider, die eine Unterklasse dieser Klasse implementieren, legen in einer XML-Datei die Funktionsweise für das Abrufen von Metadaten fest. Diese Dateien sind in System.Data.dll und System.Data.Oracleclient.dll eingebettete Ressourcen. Sie können die unformatierten XML-Dateien anzeigen, indem Sie ILDASM.exe an einer Eingabeaufforderung ausführen.
>ildasm.exe System.Data.dll /out:dummy.il
Bei der Betrachtung der von ILDASM angezeigten XML-Ressourcendateien wird ein weiterer Aspekt deutlich. In der Datei werden alle unterstützten Auflistungen sowie die in jeder Metaauflistung enthaltenen Informationen (durch das Schema) aufgelistet. Es scheint sich dabei um die Ausgabe der
DataSet.WriteXml-Methode unter Verwendung der Überladung DataSet.WriteXml(XmlWriteMode.WriteSchema) zu handeln. Am interessantesten sind die Elemente MinimumVersion/MaximumVersion in allen Metaauflistungen mit Ausnahme von DataSourceInformation sowie die Unterelemente PopulationMechanism/PopulationString in den MetaDataCollections-Elementen.
Mithilfe von MinimumVersion/MaximumVersion kann der Providerentwickler angeben, welche Metadatenabfragen für verschiedene Versionen der Datenbank ausgeführt werden sollen. Durch das Verwenden mehrerer Elemente für eine einzige MetaDataCollection können Sie GetSchema anweisen, bei verschiedenen Versionen der Datenbank unterschiedlich vorzugehen. Als einfaches Beispiel könnten Sie für SQL Server 2005 andere Versionen verwenden als für frühere Versionen von SQL Server. Es folgt ein Beispiel für den Einsatz von MinimumVersion aus der SQL Server-Metadatenressource System.Data.SqlClient.SqlMetaData:
Codebeispiel 2. Metadateneintrag für den Datentyp "XML" in der "DataTypes"-Auflistung
<DataTypes> <TypeName>xml</TypeName> <ProviderDbType>25</ProviderDbType> <ColumnSize>2147483647</ColumnSize> <DataType>System.String</DataType> <IsAutoIncrementable>false</IsAutoIncrementable> <IsCaseSensitive>false</IsCaseSensitive> <IsFixedLength>false</IsFixedLength> <IsFixedPrecisionScale>false</IsFixedPrecisionScale> <IsLong>true</IsLong> <IsNullable>true</IsNullable> <IsSearchable>true</IsSearchable> <IsSearchableWithLike>false</IsSearchableWithLike> <MinimumVersion>09.00.000.0</MinimumVersion> <IsLiteralSupported>false</IsLiteralSupported> </DataTypes>
Hiermit werden Informationen über den SQL Server-Datentyp
XML festgelegt. MinimumVersion gibt an, dass dieser Datentyp nur unter SQL Server 2005 zur Verfügung steht. Wenn Sie über SqlConnection.GetSchema eine Liste der Datentypen abrufen, die die Datenbank unterstützt, dann melden nur SQL Server 2005-Datenbanken (SQL Server 2005 ist Version 9, die aktuelle Beta 2-Version ist 09.00.852.2), dass der XML-Datentyp unterstützt wird.
Bei Auflistungen, die normalerweise durch INFORMATION_SCHEMA (beispielsweise Tabellen, Sichten oder gespeicherte Prozeduren) offen gelegt werden, sind die Elemente PopulationMechanism und PopulationString eine große Hilfe. In dieser Implementierung werden drei PopulationMechanism verwendet: DataTable, SQLCommand und PrepareCollection. Mit DataTable werden die Metaauflistungen gefüllt. Wenn Sie DataTable verwenden, dann stammen die Informationen, mit denen die Auflistung gefüllt wird, aus der XML-Ressourcendatei selbst. In jedem Fall ist PopulationString der Name der DataTable, die beim Laden der XML-Ressourcendatei in ein DataSet von .NET erstellt wird. SQLCommand bedeutet, dass der Provider eine DbCommand-Instanz verwendet, um den Befehl an die Datenbank auszugeben. Nachfolgend sehen Sie eine der PopulationStrings für eine Auflistung, die mit SQLCommand erstellt wurde:
Codebeispiel 3. Eintrag für Datenbanken (Kataloge) in SQL Server - "MetaDataCollection"
<MetaDataCollections>
<CollectionName>Databases</CollectionName>
<NumberOfRestrictions>1</NumberOfRestrictions>
<NumberOfIdentifierParts>1</NumberOfIdentifierParts>
<PopulationMechanism>SQLCommand</PopulationMechanism>
<PopulationString>select name as database_name, dbid, crdate as
create_date from master..sysdatabases where name = {0}</PopulationString>
</MetaDataCollections>
Hieraus lässt sich leicht ableiten, dass in der Basisabfrage eine Zeichenfolgenersetzung stattfindet, wenn in DbConnection.GetSchema Einschränkungen festgelegt werden. Ist dies nicht der Fall, wird dieses Prädikat vollständig aus der Abfrage entfernt.
Der Providerentwickler kann einen angepassten Mechanismus verwenden, wenn PopulationMechanism den Wert PrepareCommand hat. Es gibt eine PrepareCommand-Methode für DbMetaDataFactory, die bei Überschreibung durch den Providerentwickler so programmiert werden kann, dass sie jede vom Provider gewünschte angepasste Semantik verwendet. Dieser Mechanismus wird vom SqlClient verwendet, um die DataTypes-Metaauflistung zu erstellen. Die SqlMetaDataFactory-Unterklassenimplementierung von PrepareCommand liest zuerst (wie auch bei anderen Metaauflistungen) die integrierten und von SQL Server unterstützten Datentypen aus DataTable. Abschließend fügt sie der Auflistung anhand einer angepassten Logik benutzerdefinierte Typen hinzu, wenn es sich bei der Datenbank um SQL Server 2005 handelt. (Hinweis: SQL Server 2005 kann .NET-Klassen als benutzerdefinierte Typen offen legen. In A First Look at SQL Server 2005 for Developers (in Englisch) finden Sie in Kapitel 5 weitere Informationen hierzu.)
Anpassungen durch den Benutzer
Zusätzlich zum Anpassungsmechanismus für Provider können Programmierer anwendungsspezifische Änderungen an den Schemainformationen vornehmen. Bevor die eingebettete Ressource geladen wird, überprüft die DbConnectionFactory mit der Bezeichnung CreateMetaDataFactory die Konfigurationsdatei für die Anwendung. Jeder Provider kann CreateMetaDataFactory implementieren, um den XML-Stream für DbMetaDataFactory auf jede beliebige Weise abzurufen, allerdings folgen die vier Microsoft-Provider einem gemeinsamen Muster. Jeder Microsoft-Provider sucht nach einer Konfigurationseinstellung für die Anwendung, die denselben Namen trägt wie der Provider (z. B. system.data.sqlclient). In diesem Einstellungselement können Name/Wert-Paare hinzugefügt oder entfernt werden. DbMetaDataFactory sucht nach dem name-Wert "MetaDataXml". Der dem speziellen Namen entsprechende value ist der Dateiname. Es handelt sich dabei um einen einfachen Dateinamen - die Datei muss sich im CONFIG-Unterverzeichnis der .NET-Installation befinden. In diesem Verzeichnis werden die Datei machine.config und Konfigurationseinstellungen für die Sicherheit gespeichert. Die Datei muss den gesamten Informationssatz für die Schemakonfiguration und nicht nur die Änderungen enthalten.
Es gibt viele Gründe, diesen Mechanismus für Provider zu verwenden. Sie könnten beispielsweise die Schemaabfragen im OracleClient-Provider so ändern, dass die USER-Katalogsichten anstelle der ALL-Katalogsichten verwendet werden. Da die USER-Sichten keine Informationen zu den internen Datenbanktabellen enthalten, wird die Tables-Liste so z. B. wesentlich kürzer und überschaubarer. Eine weitere Möglichkeit wäre, Metadaten-XML-Dateien für alle .NET-Datenprovider zu schreiben, um einen einheitlichen Standardmetadatensatz bereitzustellen, etwa einen Satz, der exakt den SQL-99 INFORMATION_SCHEMA-Sichten entspricht. Das könnte genau das Richtige für Ihre Anwendung sein.
Ein konkreteres Beispiel: In SQL Server 2005 sollen Informationen über die Metadatenauflistungen von SQL Server Service Broker offen gelegt werden. Diese Auflistungen könnten QUEUE, SERVICE, CONTRACT und Nachrichtentypen enthalten. Als Ausgangspunkt kann hier die eingebettete XML-Ressourcendatei verwendet und diese um Informationen über die neuen Auflistungen erweitert werden. Wenn die Datei unter dem Namen SQLBrokerAware.xml installiert wird, ergibt sich folgende Konfigurationsdatei für die Anwendung:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.data.sqlclient> <settings> <add name="MetaDataXml" value="SQLBrokerAware.xml"></add> </settings> </system.data.sqlclient> </configuration>
Mit diesem Setup kann ein Code geschrieben werden, in dem auf die Metadaten von Service Broker wie auf die bereits integrierten Metadaten zugegriffen werden kann. Für alle Warteschlangen (Queues) kann der folgende Code verwendet werden:
using (SqlConnection conn = new SqlConnection(connstring))
{
conn.Open();
// this includes Service Broker metadata collections
Table t = conn.GetSchema(DbMetaDataCollectionNames.MetaDataCollections);
// get all the queues in my database
Table queues = conn.GetSchema("Queues");
}
Ein Codebeispiel, das Service Broker-Metadaten hinzufügt, kann mit diesem Artikel heruntergeladen werden. Dieses sehr leistungsstarke Feature kann jedoch auch missbraucht werden. Denken Sie daran, dass Sie die Metadaten-XML-Datei mit jeder Anwendung ausliefern müssen, die die Datei verwendet, und dass der Systemadministrator die Datei für Sie im CONIFG-Verzeichnis installieren muss. Außerdem müssen Sie die Datei für jede neu ausgelieferte Version des Providers aktualisieren. Da einer der Gründe für den Einsatz generischer Metadaten-APIs darin liegt, einheitliche Metadaten in verschiedenen Datenbanken und Anwendungen bereitzustellen, sollte dieses Feature nur bei Bedarf eingesetzt werden. Beachten Sie auch, dass Sie zu diesem Zeitpunkt keine angepasste Implementierung von PrepareCommand zur Verfügung stellen können.
Abschließend ist zur Anpassung Folgendes zu sagen: Sie haben sicherlich schon vermutet, dass Anpassungen und Ressourcen bei den Brückenprovidern für OLEDB und ODBC anders funktionieren. Wenn Sie diese Provider verwenden, werden die XML-Standardressourcendateien von ODBC oder OLEDB verwendet, und Sie können neben Änderungen des Schemaverhaltens von ODBC oder OLEDB auch eine Anpassung für einzelne Provider vornehmen. Wenn Sie eigene Provider oder Treiber festlegen möchten, müssen Sie zum Hinzufügen oder Entfernen von Einstellungsunterelementen statt des Namensattributes MetaDataXml das Attribut [providershortname]:MetaDataXml verwenden. Wenn Ihre Datei als Standard für den Datenprovider OLEDB oder ODBC fungieren soll, können Sie den Namen defaultMetaDataXml verwenden.
Schlussfolgerung: Abschließende Anmerkungen zur Metadatenunterstützung
Abschließend sind zwei zusätzliche Metadatenerweiterungen zu erwähnen, die nicht durch DbConnection.GetSchema offen gelegt werden. DbCommandBuilder beinhaltet die zwei Eigenschaften QuoteIdentifier und UnquoteIdentifier, mit denen Sie Bezeichner in Befehlen anpassen können, die CommandBuilder erstellt. Beispielsweise können Sie abhängig von den Sitzungseinstellungen doppelte Anführungszeichen (") oder eckige Klammern ('[' und ']') in SQL Server verwenden, um Bezeichner anzugeben. Schließlich kann die SqlMetaData-Klasse im System.Data.Sql-Namespace zum Offenlegen der Metadaten in DataReaders von SQL Server verwendet werden. Diese Metadaten können so über Parameters von SqlCommand geändert werden. Diese Metadaten ähneln vom Konzept her den in der DataTypes-Metaauflistung offen gelegten Metadaten, sind jedoch lange nicht so detailliert. SqlMetaData kann vom SqlClient-Datenprovider und dem in der Datenbank integrierten SqlServer-Datenprovider von SQL Server 2005 verwendet werden. SqlMetaData erweitert die gegenwärtig von der SqlDataReader.GetSchemaTable()-Methode offen gelegten Rowset-Metadaten.
Die Metadateninfrastruktur von ADO.NET 2.0 ist eine der leistungsstärksten, flexibelsten und anpassbarsten überhaupt. Sie erweitert die Funktionsweise von ADO.NET als einer vollständig objektorientierten Datenbank-API. Toolanbietern, Providerentwicklern und Benutzern von Visual Studio-Datentools steht damit eine Vielzahl neuer Möglichkeiten offen.
Bob Beauchemin ist Schulungsleiter, Kursautor und Berater für Datenbankschulungen für DevelopMentor. Er verfügt über mehr als 25 Jahre Erfahrung als Entwickler, Programmierer und Administrator von datenzentrierten verteilten Systemen. Er hat mehrere Artikel zu ADO.NET, OLE DB und SQL Server für das Microsoft Systems Journal, das SQL Server Magazine und andere geschrieben und ist außerdem Autor der Bücher A First Look at SQL Server 2005 for Developers (in Englisch) und Essential ADO.NET (in Englisch).
„Die Inhalte der hier eingestellten Artikel stammen möglicherweise nicht von Microsoft, sondern von Dritten und werden Ihnen kostenlos zur Verfügung gestellt. Microsoft kann daher für die Richtigkeit und Vollständigkeit der Inhalte keine Haftung übernehmen.“