Esquemas no ADO.NET 2.0

Por Bob Beauchemin - DevelopMentor

Agosto de 2004

Aplica-se a: Microsoft ADO.NET 2.0; Microsoft Visual Studio 2005; Linguagem de programação C#

Resumo: aprenda sobre o suporte avançado do ADO.NET 2.0 para acesso a metadados de suas fontes de dados.

Baixe o código de exemplo SchemasSample.exe associado.

Nesta página

Um olhar em profundidade para a nova API de metadados comuns
Quem afinal precisa de metadados?
Que metadados posso obter?
Restrições
DataSourceInformation
Personalizando e estendendo metadados
Personalização pelo usuário
Conclusão: detalhes finais sobre suporte a metadados

Um olhar em profundidade para a nova API de metadados comuns

Em meu artigo anterior, mencionei que o Server Explorer do Visual Studio 2005 utiliza atualmente uma caixa de diálogo contendo uma lista dos provedores de dados .NET (e não provedores de OLE DB) para solicitação de informações sobre conexão. Quando você se decide sobre uma seqüência de conexão e adiciona uma conexão de dados, cada conexão de dados exibe também uma árvore de informações sobre os objetos do banco de dados (como tabelas, visões e procedimentos armazenados), que são visíveis diretamente através da conexão. Mas, de onde vêm essas informações? O Visual Studio é embutido em código e produz essas informações apenas para determinados provedores de dados, deixando-me com um código vazio se eu escrever o meu próprio provedor de dados ou adquirir um de terceiros? Não, isso não acontece no Visual Studio 2005. Todas essas informações importantes são trazidas a você graças ao novo esquema de API do ADO.NET 2.0. Não sei se é assim que o Visual Studio faz isso, mas aqui está o código para obtenção de lista de tabelas do banco de dados por meio das novas APIs.

// 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");
  }
}

Quem afinal precisa de metadados?

Os metadados fazem parte de toda API de acesso a dados. Embora os principais consumidores de metadados sejam as ferramentas, como o Visual Studio, ou os geradores de código, como o DeKlarit, eles não são os seus únicos usuários. Os designers de pacotes de aplicativos podem permitir que os usuários finais personalizem um aplicativo pela adição de novas tabelas e colunas às tabelas existentes. Quando os usuários finais alteram o esquema do banco de dados dessa maneira, uma ferramenta de consulta de alteração para fins gerais pode utilizar os metadados para incluir as novas tabelas dos usuários nas tarefas de manutenção, backup ou em outras funções do aplicativo, como se fossem tabelas internas fornecidas com o aplicativo. Os programadores podem usar metadados para escrever suas próprias classes personalizadas, derivadas de System.Data.Common.DbCommandBuilder e criar comandos de inserir, atualizar e excluir para uso com o DataSet. Os desenvolvedores de aplicativos de bancos de dados diversos (ou seja, aplicativos criados para serem executados no banco de dados de preferência do usuário) podem usar metadados para manter uma base de código que seja a mais comum possível, otimizando o código de acesso a dados quando necessário.

É melhor expor os metadados através de uma API de metadados genérica do que cada consumidor ter de usar a API específica do banco de dados. Dessa maneira, os desenvolvedores de ferramentas podem manter uma base de código mais gerenciável. Esse tipo de API também precisa ser bastante flexível, porque há quatro obstáculos a se considerar ao se escrever uma API genérica para expor metadados.

1 - As coleções e informações de metadados diferem entre os bancos de dados. Por exemplo, para usuários do SQL Server, pode ser interessante expor uma coleção de servidores vinculados, enquanto os usuários do Oracle podem estar interessados em informações sobre as seqüências do Oracle.

2 - As tabelas básicas do sistema, nas quais os metadados comuns de banco de dados estão armazenados, são diferentes, não apenas nos diversos produtos de banco de dados, mas também nas diferentes versões do mesmo banco de dados. Por exemplo, o SQL Server 2005 expõe seus metadados por meio de tabelas novas em um esquema "sys" (por exemplo, sys.tables), enquanto as versões anteriores do SQL Server utilizam tabelas de metadados, como sysobjects, para armazenar esses mesmos dados.

3 - Diferentes programas podem ter diferentes visões de metadados. Por exemplo, muitos programadores se queixam das longas listas de tabelas do banco de dados Oracle, porque a maioria dos metadados de API misturam tabelas "system" com tabelas do usuário. Eles gostariam de ter uma lista pequena que consistisse apenas nas tabelas definidas por eles.

4 - Oferecer ou não suporte a metadados, e em que quantidade fazê-lo deve ficar inteiramente a critério do desenvolvedor do provedor.

A maior parte das APIs de banco de dados fornece um conjunto padrão de metadados, ao qual todos os provedores devem oferecer suporte, que permite que os desenvolvedores de provedor adicionem novas tabelas de metadados. Essa abordagem é consistente com aquela adotada pelo padrão ANSI SQL. A parte do padrão que trata disso é a Schema Schemata (INFORMATION_SCHEMA e DEFINITION_SCHEMA), parte 11. O ANSI SQL INFORMATION_SCHEMA define o conjunto padrão de visões de metadados que deve ter suporte em um banco de dados compatível. Porém, até mesmo a especificação precisa ter uma forma de tratar os pontos acima. Ela diz que:

"Os implementadores têm liberdade de acrescentar tabelas adicionais ao INFORMATION_SCHEMA ou colunas adicionais às tabelas INFORMATION_SCHEMA predefinidas."

Como exemplo de uma API de acesso a dados compatível com o padrão ANSI SQL, o OLE DB definiu uma série de metadados, que é denominada "Schema Rowsets". Ele partiu de um conjunto predefinido que, em termos gerais, seguia o INFORMATION_SCHEMA e adicionou colunas específicas do OLE DB a cada um. O ADO.NET 2.0 oferece um mecanismo ainda mais eficaz e flexível para a exposição de metadados.

Que metadados posso obter?

O ADO.NET 2.0 permite que o desenvolvedor de provedor exponha cinco tipos diferentes de metadados. Esses principais tipos de metadados, "metacoleções" ou "categorias", são enumerados no System.Data.Common.DbMetaDataCollectionNames.

  • MetaDataCollections — Uma lista das coleções de metadados disponíveis.

  • Restrictions — Para cada uma das coleções de metadados, uma matriz de qualificadores que pode ser usada para restringir o escopo das informações de esquema solicitadas.

  • DataSourceInformation — Informações sobre a instância do banco de dados à qual o provedor faz referência.

  • DataTypes — Um conjunto de informações sobre cada tipo de dado a que o banco de dados oferece suporte.

  • ReservedWords — Palavras reservadas para determinada linguagem de consulta de banco de dados. Normalmente, a "linguagem de consulta" equivale a um dialeto do SQL.

MetaDataCollections é o nome das coleções INFORMATION_SCHEMA, como "Tables", "Columns" ou "PrimaryKeys". Contudo, quando se utiliza o DbConnection.GetSchema, essas categorias de metadados são também consideradas metadados. O que isso significa em termos de código é que essas coleções podem ser obtidas como metadados comuns.

// 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();

Duas das cinco coleções de metadados merecem explicações mais detalhadas.

Restrições

As restrições podem ser usadas para limitar a quantidade de metadados retornados. Se você está familiarizado com o OLE DB ou o ADO, o termo "restrição" tem o mesmo significado nessas APIs. Como exemplo, tomemos a MetaDataCollection "Columns", que é o conjunto de nomes de colunas nas tabelas. Essa coleção pode ser usada para obtenção de todas as colunas em todas as tabelas. Porém, o conjunto de colunas solicitado pode ser restingido por nome do banco de dados, por proprietário/esquema ou por tabela. Cada coleção de metadados pode ter um número diferente de restrições possíveis e cada restrição pode ter um padrão. Prosseguindo com o nosso exemplo, temos a seguir uma representação em XML das restrições dos metadados de Columns:

Listagem 1. Restrições na coleção Columns (formato XML)

<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>

As restrições são especificadas por meio de uma sobrecarga de DbConnection.GetSchema. Elas são especificadas como uma matriz. É possível especificar uma matriz tão grande quanto coleções inteiras de restrições ou uma matriz de subconjunto, porque os "RestrictionNumbers", em geral, vão do menos restritivo ao mais restritivo. Utilize um valor nulo (não banco de dados NULL, mas .NET null ou Nothing no Visual Basic .NET) como valores de restrição a serem deixados de fora. Por exemplo:

// 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);

Não é necessário especificar toda a matriz de restrições. No caso acima, onde você deseja ter apenas colunas em tabelas de propriedade do "dbo", é possível especificar uma matriz com apenas dois membros em vez de todos os quatro. Observe também que especificar uma seqüência de caracteres vazia como restrição é diferente de especificar um valor nulo (Nothing no Visual Basic .NET). Não é necessário memorizar as restrições; sempre será possível consultá-las, exatamente como acontece com qualquer outra coleção. A coleção "Restrictions", em si, não permite restrições, porém, como as informações são buscadas em uma DataTable, uma DataView pode ser usada para fornecer uma funcionalidade semelhante, como mostrado abaixo.

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"]);

DataSourceInformation

A coleção DataSourceInformation dá informações sobre a instância atual do banco de dados (fonte de dados) aos construtores de consultas. Embora essa coleção possa conter o que o provedor quiser, nos provedores Microsoft (SqlClient, OracleClient, OleDb, Odbc) ela contém informações similares. Estas são as informações que você obtém por padrão.

Tabela 1. DataSourceInformation em provedores Microsoft

Valor

Formato/significado

CompositeIdentifierSeparatorPattern

Delimitador para nomes com várias partes (p. ex.: o ponto em pubs.dbo.authors)

DataSourceProductName

Nome do banco de dados

DataSourceProductVersion

Versão do banco de dados. Observe que se trata da versão da instância do banco de dados sendo atualmente acessada por DbConnection.

DataSourceProductVersionNormalized

 

GroupByBehavior

Enumeração, System.Data.Common.GroupByBehavior

IdentifierPattern

Seqüência de caracteres de expressão comum

IdentifierCase

Enumeração, System.Data.Common.GroupByBehavior

OrderByColumnsInSelect

Booleano, se você ordenar (ORDER BY) pelas colunas em uma instrução SELECT por padrão

ParameterMarkerFormat

Indica se os marcadores de parâmetros começam com um caractere especial (p. ex., @ para T-SQL)

ParameterMarkerPattern

Seqüência de caracteres de expressão comum, usada para criar parâmetros

ParameterNameMaxLength

Comprimento máximo de um parâmetro

ParameterNamePattern

Seqüência de caracteres de expressão comum, usada para criar parâmetros

QuotedIdentifierPattern

Seqüência de caracters de expressão comum, usada para citar identificadores

QuotedIdentifierCase

Enumeração, System.Data.Common.GroupByBehavior

StatementSeparatorPattern

Seqüência de caracteres de expressão comum

StringLiteralPattern

Seqüência de caracteres de expressão comum

SupportedJoinOperators

Enumeração, System.Data.Common.GroupByBehavior

Essas informações são mais do que suficientes para produzir o SQL para um determinado dialeto de banco de dados, você não acha? Há apenas mais uma informação que eu gostaria de obter: se o provedor usa parâmetros nomeados ou parâmetros posicionais em consultas parametrizadas. Eu menciono os parâmetros nomeados e posicionais como duas formas de escrever comandos parametrizados em meu artigo anterior sobre escrita de código independente de provedor.

Personalizando e estendendo metadados

Agora que vimos os metadados básicos fornecidos e que já podemos nos situar com relação ao DbConnection.GetSchema(), vamos observar as maneiras como os desenvolvedores de provedor podem personalizar metadados usando um formato declarativo simples e como os programadores podem aproveitar esse formato. Esta discussão retoma as complicações relativas a metadados citadas no começo deste artigo: como fornecer metadados independentes da versão do banco de dados, e como lidar com o fato de que diferentes clientes podem querer versões diferentes dos mesmos metadados.

Primeiramente, ressaltemos que o suporte a metadados é absolutamente opcional. Os provedores não têm que oferecer suporte ao DbConnection.GetSchema; esse método pode provocar uma NotSupportedException. Além disso, apenas a categoria MetaDataCollections é necessária se o desenvolvedor de provedor optar por oferecer suporte ao DbConnection.GetSchema. Os provedores podem optar por não suprir algumas ou todas as quatro categorias de informações.

Além disso, cada provedor pode expor diferentes informações da mesma coleção de metadados. A estrutura da coleção Tables, por exemplo, fica totalmente a critério do desenvolvedor de provedor. Por exemplo, o provedor SqlClient expõe quatro itens de informação na coleção Tables: table_catalog, table_schema, table_name e table_type. O provedor OracleClient expõe apenas três itens de informações (OWNER, TABLE_NAME e TYPE), porque os bancos de dados Oracle não contêm múltiplos catálogos. O número de restrições e os itens de restrição podem ser diferentes para cada provedor. Utilizando o caso das Tables novamente, o provedor SqlClient oferece suporte a quatro restrições, e o provedor de OracleClient a duas. As restrições também não têm que ocorrer em uma ordem específica. Assim, não existe uma estrutura, uma quantidade ou uma ordem de metadados obrigatória, como há nas APIs do OLE DB ODBC. O provedor fica livre para expor os metadados que forem pertinentes. No entanto, se um aplicativo específico, (como o Visual Studio), exigir que os metadados sejam consistentes entre todos os provedores de dados .NET usados no aplicativo, esse comportamento pode ser obtido também pela substituição do comportamento normal do provedor. Isso é discutido com mais detalhes na seção Personalização pelo usuário.

Os desenvolvedores de provedor podem incluir a lógica dos metadados diretamente no código dos seus provedores, com cada desenvolvedor de provedor utilizando um algoritmo interno potencialmente diferente para a obtenção de metadados similares. Era assim que se fazia no passado, por exemplo, na implementação do método do OLE DB ISchemaRowset. No ADO.NET 2.0, contudo, há algumas classes básicas à disposição dos desenvolvedores de provedor no espaço para nome System.Data.ProviderBase. Os quatro provedores Microsoft disponíveis utilizam essas classes básicas, de modo que todos implementam os esquemas de maneira semelhante. Usarei essa implementação para exposição, esperando que os principais desenvolvedores de provedor, como a DataDirect Technologies e outros, também a utilizem.

A classe básica para exposição de metadados é DbMetaDataFactory. Os provedores que implementam uma subclasse dela usam um arquivo XML para definir seu comportamento de busca de metadados. Esses arquivos são recursos internos de System.Data.dll e System.Data.OracleClient.dll. É possível ver os arquivos brutos XML executando ILDASM.exe na linha de comando.

>ildasm.exe System.Data.dll /out:dummy.il

Observando os resultados dos arquivos de recursos XML do ILDASM, descemos mais uma camada. O arquivo enumera as coleções que têm suporte e as informações contidas em cada metacoleção (por meio do esquema) e parece consistir na saída do método DataSet.WriteXml utilizando a sobrecarga do DataSet.WriteXml(XmlWriteMode.WriteSchema). Os bits mais interessantes são os elementos MinimumVersion/MaximumVersion em todas as metacoleções, exceto os subelementos DataSourceInformation e PopulationMechanism/PopulationString nos elementos MetaDataCollections.

Utilizar MinimumVersion/MaximumVersion permite que o autor de provedor especifique as consultas de metadados a serem executadas para as diferentes versões do banco de dados. Por meio dos vários elementos de uma única MetaDataCollection, é possível fazer com que o GetSchema se comporte diferentemente para diferentes versões do banco de dados. Como um exemplo óbvio, seria possível usar versões diferentes do SQL Server 2005 em vez de versões anteriores do SQL Server. A seguir, um exemplo de utilização de MinimumVersion a partir do recurso de metadados do SQL Server, System.Data.SqlClient.SqlMetaData:

Listagem 2. Entrada de metadados para tipo de dado XML em coleção de tipos de dado

<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>

Isso define as informações sobre o tipo de dado XML do SQL Server. A MinimumVersion indica que esse tipo de dado só se encontra disponível quando se usa o SQL Server 2005. Se você pedir ao SqlConnection.GetSchema uma lista de tipos de dados que têm suporte no banco de dados, somente os bancos de dados SQL Server 2005 (SQL Server 2005 é versão 9, a versão 2 beta atual é a 09.00.852.2) vão relatar que oferecem suporte ao tipo de dado XML.

No caso de coleções normalmente expostas por INFORMATION_SCHEMA (como Tables, Visions e Stores Procedures), PopulationMechanism e PopulationString são o início de tudo. Três PopulationMechanisms são usados nessa implementação: DataTable, SQLCommand e PrepareCollection. O DataTable é usado para preencher as metacoleções. Utilizar DataTable significa que as informações usadas para preencher a coleção encontram-se no próprio arquivo de recursos XML. Em cada caso, a PopulationString é o nome da DataTable produzida quando o arquivo de recursos XML é carregado em um .NET DataSet. SQLCommand indica que o provedor usará uma instância do DbCommand para emitir o comando com relação ao banco de dados. Se observarmos uma das PopulationStrings de uma coleção produzida por um SQLCommand:

Listagem 3. Entrada para bancos de dados (Catalogs) no 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>

É relativamente fácil deduzir que a substituição de seqüência de caracteres é aplicada à "consulta básica" quando restrições são usadas no DbConnection.GetSchema. Se nenhuma restrição for especificada, esse predicado será efetivamente retirado da consulta.

O autor de provedor pode usar um mecanismo personalizado quando o valor de PopulationMechanism é PrepareCommand. Há um método PrepareCommand do DbMetaDataFactory que, se sobrescrito pelo autor de provedor, pode ser codificado para utilizar qualquer semântica de personalização que o provedor escolher. Esse mecanismo é usado no SqlClient para produzir a metacoleção DataTypes. A implementação da subclasse SqlMetaDataFactory de PrepareCommand lê primeiramente os tipos de dados internos, com suporte no SQL Server, na DataTable, como faz com as outras metacoleções, e depois usa a lógica personalizada para adicionar tipos definidos pelo usuário à coleção, se o banco de dados for o SQL Server 2005. (Observação: o SQL Server 2005 pode expor as classes do .NET como tipos definidos pelo usuário. Consulte o capítulo 5 do A First Look at SQL Server 2005 for Developers para obter mais informações.)

Personalização pelo usuário

Além do mecanismo de personalização pelo provedor, há ainda um gancho que permite que os programadores personalizem as informações de esquema aplicativo por aplicativo. Antes de carregar o recurso interno, o DbConnectionFactory CreateMetaDataFactory consulta o arquivo de configuração do aplicativo. Cada provedor pode implementar o CreateMetaDataFactory para recuperar o fluxo de XML para o seu DbMetaDataFactory em qualquer uma das formas escolhidas, mas os quatro provedores Microsoft seguem um padrão comum. Todo provedor Microsoft procura uma configuração de aplicativo que tenha o mesmo nome do provedor (p. ex.: system.data.sqlclient). É possível adicionar ou remover pares de valor de nome desse elemento de configuração. DbMetaDataFactory procura um name "MetaDataXml". O value que corresponde ao nome especial é o nome de um arquivo. Trata-se de um nome de arquivo simples, que deve existir no subdiretório CONFIG do local onde o .NET está instalado. Esse é o diretório onde se encontram o machine.config e as configurações de segurança. Esse arquivo deve conter o conjunto total de informações de configuração de esquema, não apenas as alterações.

Você pode usar esse mecanismo, para os provedores que oferecem suporte a ele, por diversos motivos. Por exemplo, as consultas de esquema podem ser modificadas no provedor OracleCliente para usar as visões do catálogo USER em lugar das visões do catálogo ALL. Como as visões USER não contêm informações sobre as tabelas internas do banco de dados, a lista de Tables, por exemplo, será muito menor e mais fácil de utilizar. Um outro exemplo pode consistir em colocar no código arquivos de metadados XML de todos os provedores de dados .NET que fornecem um padrão consistente de metadados, possivelmente um que corresponda exatamente às visões do SQL-99 INFORMATION_SCHEMA. Isso pode ser o ideal para o seu aplicativo.

Um exemplo mais concreto seria se eu quisesse expor informações sobre as coleções de metadados do SQL Server Service Broker no SQL Server 2005. Essas coleções poderiam incluir QUEUEs, SERVICEs, CONTRACTs e tipos de mensagem. Eu começaria com o arquivo de recursos XML interno e o aperfeiçoaria com informações sobre minhas novas coleções. Se o nome do arquivo fosse SQLBrokerAware.xml, eu o instalaria, e a configuração do meu aplicativo seria a seguinte:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.data.sqlclient>
    <settings>
       <add name="MetaDataXml" value="SQLBrokerAware.xml"></add>
    </settings>
  </system.data.sqlclient>
</configuration>

E isso é tudo! Utilizando essa instalação, eu poderia escrever o código no qual os metadados do Service Broker seriam parte dos metadados internos disponíveis para o cliente. O código poderia ficar assim em todas as Queues:

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");
}

Realmente muito legal! Um exemplo de código que adiciona metadados do Service Broker acompanha este artigo. Embora esse seja um recurso muito eficaz, existe realmente a possibilidade de uso indevido. Lembre-se de que será necessário distribuir o arquivo de metadados XML a todos os aplicativos que o utilizam e convencer o administrador do sistema a instalá-lo no diretório CONFIG para você. E será necessário mantê-lo em cada nova versão do provedor que for lançada. Como uma das finalidades das APIs de metadados genéricas é ter metadados consistentes em todos os bancos de dados e aplicativos, esse recurso não deve ser usado de forma impensada. Observe também que não é possível fornecer uma implementação personalizada do PrepareCommand nesse momento.

Como recomendação final sobre personalização, você já deve ter adivinhado que a personalização e os recursos funcionam diferentemente com os fornecedores de ponte para o OLE DB e o ODBC. Quando você usa esses provedores, os arquivos de recursos padrão do Odbc ou do OleDb XML são usados, e é possível personalizar não apenas os principais comportamentos de esquema do Odbc ou do OleDb, como também personalizar o comportamento para cada provedor. Se você desejar especificar seus próprios provedores ou drivers, o atributo de nome usado para adicionar/remover subelementos de configurações não seria MetaDataXml, mas [apelidodoprovedor]:MetaDataXml. Se você desejar que um arquivo seja o padrão para o provedor de dados OleDb ou Odbc, é possível até mesmo especificar um nome de defaultMetaDataXml.

Conclusão: detalhes finais sobre suporte a metadados

Finalizando, gostaria de mencionar duas outras extensões de metadados que não são expostas através do DbConnection.GetSchema. O DbCommandBuilder tem duas propriedades, QuoteIdentifier e UnquoteIdentifier, que permitem a personalização de identificadores em comandos criados pelo CommandBuilder. Por exemplo, no SQL Server, é possível usar aspas duplas (") ou colchetes ('[' e ']') para citar identificadores, dependendo das configurações da sessão. Finalmente, a classe SqlMetaData, no espaço para nome System.Data.Sql, é usada para expor os metadados do SQL Server DataReaders e permite definir esses metadados em Parameters utilizados pelo SqlCommand. Esses metadados são similares em conceito, embora não se comparem em nível de detalhes, aos metadados expostos nas metacoleções de tipos de dado. O SqlMetaData é utilizável por ambos, o provedor de dados SqlClient e o provedor de dados internos do banco de dados SqlServer, incluídos no SQL Server 2005. O SqlMetaData amplia os metadados de conjunto de linhas expostos atualmente pelo método SqlDataReader.GetSchemaTable().

Nesse ponto, você provavelmente concorda que a infra-estrutura de metadados do ADO.NET 2.0 é a mais eficaz, flexível e personalizável que você já viu. Ela expande a visão do ADO.NET como uma API completa de banco de dados orientado a objeto. Fornecedores de ferramentas, desenvolvedores de provedor e usuários de ferramentas de dados do Visual Studio ficarão exultantes. A gente se vê na comemoração.

Bob Beauchemin é instrutor, autor de cursos e contato de cursos sobre bancos de dados da DevelopMentor. Tem mais de vinte e cinco anos de experiência como arquiteto, programador e administrador de sistemas distribuídos centrados em dados. Escreveu artigos sobre ADO.NET, OLE DB e SQL Server para o Microsoft Systems Journal e a SQL Server Magazine, entre outros, e é o autor dos livros A First Look at SQL Server 2005 for Developers e Essential ADO.NET.