![]() |
||||||||
|
|
||||||||


| com exemplos em VB |
| Componente para deixar forms em Vb semelhantes às telas do winnamp |
| Componente para colocar sua aplicação VB no Systray |
| Componente para transformar sua aplicação VB em serviço |
| Ferramentas úteis para quem usa Olap Server |
| |

|
|
Pesquisa personalizada
Imprimir Baixe os fontes Obtendo Schemas de base de dados utilizando o ADO.NET Existem muitas aplicações que precisam obter informações sobre a estrutura de objetos na base de dados, isso é algo bem frequente. Aplicações para geração de código, por exemplo, precisam de amplas informações sobre a estrutura das bases de dados. O data provider para OLEDB implementa recursos para obtermos a estrutura de bases de dados. Como trata-se de um data provider ligado a inúmeros bancos de dados (através dos providers OLEDB), podemos obter a estrutura de inúmeros tipos de banco de dados diferentes. Toda essa possibilidade encontra-se no método chamado GetOleDbSchemaTable. Este método recebe 2 parâmetros : Um GUID que identifica o tipo de objeto que desejamos (tabelas, procedures, views, etc..) e um 2o parâmetro que atua como filtro para os dados que serão retornados. Este método devolve uma dataTable, tornando simples a manipulação dos dados no client. Vamos então montar um pequeno projeto que nos permita acessar e trabalhar com os dados sobre a estrutura do banco, demonstrando desta forma como manipular estes dados, entre outros exemplos úteis. Vamos inicialmente criar uma Windows Application com um form e colocar dois objetos no form : Uma listBox (lstObjetos) e uma DataGrid (dg). Vamos preencher a listbox com uma lista dos tipos de objetos que poderemos estar requisitando da base de dados. Mas este preenchimento não será tão simples : Os tipos de objetos não estão em um enum, estão disponíveis na forma de propriedades da classe System.Data.OleDb.OleDbSchemaGuid, cada uma devolvendo o GUID do tipo de objeto correspondente. Precisaremos então utilizar Reflections para podermos preencher a listbox, veja : 58 Dim p As New ArrayList 59 Dim t As Type 60 t = GetType(System.Data.OleDb.OleDbSchemaGuid) 61 p.AddRange(t.GetFields) 62 lstObjetos.DataSource = p 63 lstObjetos.DisplayMember = "Name" Desta forma vinculamos a listbox a um arrayList de objetos Field, que devolvem os campos existentes na classe OleDbSchemaGuid.
Sempre que houver um click na listbox vamos obter os dados referentes a este tipo de objeto e vamos exibir tais dados na dataGrid. A lista de objetos, porém, é genérica. Portanto nem todos os objetos estão disponíveis para todos os tipos de banco, precisaremos tratar isso também. Veja como fica : 58 Dim ds As New DataSet 59 Dim nomeTabela As String 60 nomeTabela = DirectCast(lstObjetos.SelectedItem, FieldInfo).Name 61 62 63 Dim dt As DataTable 64 CN.Open() 65 Try 66 dt = CN.GetOleDbSchemaTable(DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid)), Nothing) 67 Catch er As Exception 68 MsgBox("Este tipo de elemento não está disponível neste provider OLEDB" & vbCrLf & er.Message) 69 Exit Sub 70 Finally 71 CN.Close() 72 End Try 73 74 ds.Tables.Add(dt) 75 76 77 dg.DataSource = ds.Tables(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) Observe a expressão, um pouco mais complexa, utilizada como primeiro parâmetro no método GetOledbSchemaTable. Tendo o objeto FieldInfo, precisamos utilizar o método GetValue deste objeto para obter o valor de uma propriedade. Porém como as propriedades em questão são Shared, a aplicação do GetValue recebe como parâmetro um type, a própria classe OleDbSchemaGuid .
Pronto. Com essa codificação simples já temos acesso a informações de estrutura da base de dados. Agora vamos tornar esta aplicação um pouco mais sofisticada. Vamos adicionar a possibilidade do usuário fazer personalizações no que é exibido (por exemplo, cortando tipos de objetos desnecessários na listbox e colunas desnecessárias na grid) e manter a configuração feita pelo usuário entre as execuções da aplicação. Vamos começar com a listbox. Além de possibilitar a deleção de itens da listbox precisaremos guardar os itens que foram deletados em um arraylist, o arrayList dos deletados. Poderemos serializar este arraylist para um arquivo em disco e recupera-lo posteriormente, quando a aplicação for novamente aberta. Então vamos ver como isso fica : 86 Private Sub lstObjetos_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles lstObjetos.KeyDown 87 If e.KeyData = Keys.Delete Then 88 Dim c As CurrencyManager 89 ar.Add(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) 90 remover(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) 91 92 c = Me.BindingContext(p) 93 c.SuspendBinding() 94 c.ResumeBinding() 95 96 End If 97 End Sub Observe que temos então 2 arrayLists : Um arraylist com os elementos exibidos e um arraylist para guardar os elementos deletados. No exemplo acima adicionamos o nome do item deletado no arrayList de deletados (AR) e eliminamos o item deletado do array de itens exibidos. Para isso criei uma sub a parte chamada remover. Quanto ao uso do CurrencyManager, acima, isso se deve ao fato de que uma listbox, quando vinculada a um arraylist, não se atualiza automaticamente quando o arrayList se atualiza. Esse assunto foi tema de uma dica no site BufaloInfo, veja em http://www.bufaloinfo.com.br/dicas.asp?cod=725 Veja como fica a sub Remover : 99 Private Sub remover(ByVal nome As String) 100 For Each f As FieldInfo In p 101 If f.Name = nome Then 102 p.Remove(f) 103 Exit Sub 104 End If 105 Next 106 End Sub Como o arrayList ligado a combo possui objetos FieldInfo foi necessário localizar o objeto FieldInfo correto para poder elimina-lo. Agora vejamos como fica o processo de serialização e deserialização do arrayList. Este código é especialmente interessante pois esta tarefa pode ser realizada com qualquer objeto que tenha capacidade de serialização - e são muitos. Veja o código de serialização : 58 If File.Exists(NomeHash) Then 59 File.Delete(NomeHash) 60 End If 61 Dim obj As New BinaryFormatter 62 Dim st As New FileStream(NomeHash, FileMode.Create) 63 obj.Serialize(st, ar) 64 st.Close() Como trata-se da gravação de um arquivo em disco, tomei o cuidado de verificar se o arquivo existe e, caso exista, deleta-lo. Com a classe BinaryFormatter (que fica em System.Runtime.Serialization.Formatters.Binary ) fica fácil fazer o processo de serialização e deserialização de um objeto, como pode observar acima. O FileStream, localizado em System.IO, completa o trabalho fazendo a gravação do resultado da serialização em um arquivo em disco. Veja como fica a deserialização, tão simples quanto : 59 If File.Exists(NomeHash) Then 60 Dim obj As New BinaryFormatter 61 Dim st As New FileStream(NomeHash, FileMode.Open) 62 ar = obj.Deserialize(st) 63 st.Close() 64 For Each f As String In ar 65 remover(f) 66 Next 67 End If Observe que após a deserialização já implementei o que desejamos : faço uma varredura no arraylist e a eliminação dos elementos deletados do arraylist principal. Neste trecho de código utilizei um truque interessante : Em todos os pontos nos quais me refiro ao nome do arquivo utilizei uma chamada a uma propriedade, NomeHash. O nome do arquivo propriamente é uma montagem que depende do local onde a aplicação estiver. Utilizando uma propriedade, NomeHash, centralizo em um único ponto do código a geração do nome do arquivo. Assim sendo se em algum momento for necessário alterar o código de geração do nome deste arquivo bastará altera-lo em um único local. Veja como fica a propriedade : 98 Private ReadOnly Property NomeHash() As String 99 Get 100 Return (Application.StartupPath & "\hash.bin") 101 End Get 102 End Property Vamos agora permitir que o usuário personalize as colunas na DataTable e mantenha essa personalização através das execuções da aplicação. Para permitir a deleção de colunas na grid vamos criar um contextMenu. Porém para que possamos saber em que coluna o mouse estava no momento em que o contextMenu for ativado não irei vincula-lo diretamente na propriedade contextMenu da grid, mas sim ativa-lo através do evento mouseUp. Veja como fica : 104 Private Sub dg_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles dg.MouseUp 105 If e.Button = MouseButtons.Right Then 106 pt = New Point(e.X, e.Y) 107 ContextMenu1.Show(dg, pt) 108 End If 109 End Sub Observe que guardei na variável pt um objeto point com as coordenadas do click. Desta forma poderemos utilizar este objeto para descobrir qual foi a coluna clicada. Veja como fica o click do menu : 111 Private Sub MenuItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItem1.Click 112 113 Dim hit As DataGrid.HitTestInfo 114 hit = dg.HitTest(pt) 115 DirectCast(dg.DataSource, DataTable).Columns.RemoveAt(hit.Column) 116 117 End Sub Através do método HitTest podemos obter informações sobre um determinado ponto com relação a datagrid, descobrindo, por exemplo, qual foi a coluna clicada, para remove-la em seguida como é feito no código acima. Partimos então para o próximo problema : Como manter a escolha das colunas através das execuções da aplicação ? A melhor forma de fazer isso é guardando o schema do DataSet em um arquivo em disco. Precisaremos então fazer pequenas adaptações na forma de carregar o dataSet : No nosso exemplo anterior, simplesmente adicionavamos a dataTable. Agora precisaremos manter um dataSet único e chegar se a dataTable já existe ou não. Se não existe, simplesmente adicionamos, mas se já existe então precisamos preencher os dados dentro da estrutura da dataTable já existente, fazendo um Merge. Veja como fica : 119 Private Sub LerSchema(ByVal nome As System.Guid) 120 Dim dt As DataTable 121 CN.Open() 122 Try 123 dt = CN.GetOleDbSchemaTable(nome, Nothing) 124 Catch er As Exception 125 MsgBox("Este tipo de elemento não está disponível neste provider OLEDB" & vbCrLf & er.Message) 126 Exit Sub 127 Finally 128 CN.Close() 129 End Try 130 If Not ds.Tables.Contains(dt.TableName) Then 131 ds.Tables.Add(dt) 132 Else 133 ds.Merge(dt, False, MissingSchemaAction.Ignore) 134 End If 135 End Sub Observe no código acima que o Merge faz uso do MissingSchemaAction. Isso porque o usuário pode ter eliminado alguns campos da tabela. Se o Merge não definisse essa opção os campos seriam re-adicionados. Outra questão interessante é que neste ponto já transformei essa rotina em uma sub LerSchema. Essa sub é chamada do SelectedIndexChanged da listbox. Veja como fica : 137 Private Sub lstObjetos_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstObjetos.SelectedIndexChanged 138 139 Dim nomeTabela As String 140 nomeTabela = DirectCast(lstObjetos.SelectedItem, FieldInfo).Name 141 If Not ds.Tables.Contains(nomeTabela) Then 142 143 LerSchema(DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid))) 144 ElseIf ds.Tables(nomeTabela).Rows.Count = 0 Then 145 LerSchema(DirectCast(lstObjetos.SelectedItem, FieldInfo).GetValue(GetType(System.Data.OleDb.OleDbSchemaGuid))) 146 End If 147 148 dg.DataSource = ds.Tables(DirectCast(lstObjetos.SelectedItem, FieldInfo).Name) 149 150 End Sub Se a tabela não existe no dataSet ou se está sem linhas é chamada a sub LerSchemas, do contrário é porque os dados já haviam sido carregados antes, então a tabela é apenas exibida novamente. Serializar e deserializar o schema deste dataSet será uma tarefa bem simples, mais que no caso anterior : O dataSet não precisa que utilizemos uma classe de serialização, como antes, ele próprio possui métodos para fazer a serialização do schema.
Neste ponto já conseguimos permitir que o usuário personalize a aplicação e que esta personalização seja mantida, o que já torna a aplicação bem interessante. Mas para que a aplicação fique ainda mais útil torna-se necessário permitir que a aplicação guarde não apenas uma, mas diversas configurações nomeadas. Assim sendo o usuário pode se conectar a diferentes bancos de dados e guardar as configurações de como deseja visualizar cada um. Vamos começar criando uma classe Configuracao para agregar os dados que cada configuracao deve ter. Uma configuração terá um nome, uma string de conexão e irá também gerar um código que será anexado aos nomes de arquivos que guardarão as características da configuração. Veja como fica : 152 <Serializable()> Public Class config 153 Public NomeConfig As String 154 Public Conexao As String 155 Private _id As String 156 157 Public Sub New() 158 159 End Sub 160 161 Public Sub New(ByVal cf As String, ByVal con As String, ByVal id As Integer) 162 NomeConfig = cf 163 Conexao = con 164 End Sub 165 166 Public ReadOnly Property CodigoArquivo() As String 167 Get 168 Return (NomeConfig.Substring(0, 3) & _id) 169 End Get 170 End Property 171 172 Public Overrides Function tostring() As String 173 Return (NomeConfig) 174 End Function 175 176 End Class Criei um construtor recebendo parâmetros, para simplificar a configuração desta classe. Uma propriedade readonly chamada CodigoArquivo, que irá gerar uma pequena string a ser anexada o nome dos arquivos. A função ToString levou um overrides para podermos definir o que desejamos que apareça em uma combo quando esta classe for inserida em uma combo. Vamos então preparar este formulário que temos para poder lidar com essa classe. Esse formulário irá receber uma instância desta classe como parâmetro quando for aberto (o usuário já terá escolhido a configuração - faremos isso depois) e deverá utilizar as informações contidas nesta classe. Primeiramente, uma propriedade para controlar o recebimento desta classe : 178 Public Property configuracao() As frmConfig.config 179 Get 180 Return (_Configuracao) 181 End Get 182 Set(ByVal Value As frmConfig.config) 183 _Configuracao = Value 184 End Set 185 End Property As propriedades que geram os nomes dos arquivos passarão a utilizar esta propriedade configuração, veja : 187 Private ReadOnly Property NomeSchema() As String 188 Get 189 Return (Application.StartupPath & "\schema" & Configuracao.CodigoArquivo & ".xml") 190 End Get 191 End Property 192 193 Private ReadOnly Property NomeHash() As String 194 Get 195 Return (Application.StartupPath & "\hash" & Configuracao.CodigoArquivo & ".bin") 196 End Get 197 End Property Podemos também exibir no título do form o nome da configuração selecionada : 59 Me.Text = Me.configuracao.NomeConfig Precisaremos de um botão que permita ao usuário trocar a configuração selecionada. Neste caso o ideal será transformarmos a inicialização do form (deserializações) e a finalização (serializações) em subs que possam ser chamadas de mais de um local. Veja como fica : 198 Sub inicializarForm() 199 Dim t As Type 200 201 salvarconfig() 202 203 Dim c As CurrencyManager 204 c = Me.BindingContext(p) 205 c.SuspendBinding() 206 207 ar.Clear() 208 p.Clear() 209 210 ds = New DataSet 211 lstObjetos.DataSource = Nothing 212 lstObjetos.Items.Clear() 213 214 Me.Text = Me.configuracao.NomeConfig 215 216 t = GetType(System.Data.OleDb.OleDbSchemaGuid) 217 p.AddRange(t.GetFields) 218 219 If File.Exists(NomeHash) Then 220 Dim obj As New BinaryFormatter 221 Dim st As New FileStream(NomeHash, FileMode.Open) 222 ar = obj.Deserialize(st) 223 st.Close() 224 For Each f As String In ar 225 remover(f) 226 Next 227 End If 228 229 If IO.File.Exists(NomeSchema) Then 230 ds.ReadXmlSchema(NomeSchema) 231 End If 232 233 234 lstObjetos.DataSource = p 235 lstObjetos.DisplayMember = "Name" 236 c.ResumeBinding() 237 End Sub 238 239 Public Sub salvarconfig() 240 ds.WriteXmlSchema(NomeSchema) 241 242 243 If File.Exists(NomeHash) Then 244 File.Delete(NomeHash) 245 End If 246 Dim obj As New BinaryFormatter 247 Dim st As New FileStream(NomeHash, FileMode.Create) 248 obj.Serialize(st, ar) 249 st.Close() 250 End Sub Desta forma podemos chamar essas subs do evento Load, closing, do botão e uma chama a outra (ao trocar a configuração, salva-se a anterior). Vamos então criar um 2o form, que permitirá ao usuário selecionar a configuração desejada ou criar uma nova configuração. A aplicação passará a ser disparada por uma sub main que interligará os dois forms, veja : 253 Module Module1 254 Public Sub Main() 255 Dim frm As New frmConfig 256 Dim f As New Form1 257 frm.ShowDialog() 258 If frm.DialogResult = DialogResult.OK Then 259 f.configuracao = frm.Configuracao 260 Application.Run(f) 261 End If 262 End Sub 263 End Module No formulário para escolha da configuração, vamos chama-lo de frmConfig, vamos inserir uma combo para listar as configurações existentes, um botão para disparar a criação de uma nova configuração e um botão para dar ok na seleção de uma configuração e seguir adiante.
Teremos mais um arquivo serializado, que conterá a lista de configurações existentes. Precisaremos no load do form fazer a deserialização deste arquivo. Veja como fica : 59 If File.Exists(arquivoConfig) Then 60 Dim obj As New BinaryFormatter 61 Dim st As New FileStream(arquivoConfig, FileMode.Open) 62 hs = obj.Deserialize(st) 63 st.Close() 64 End If 65 cmbConfigs.DataSource = hs 258 Public ReadOnly Property arquivoConfig() As String 259 Get 260 Return (Application.StartupPath & "/configs.bin") 261 End Get 262 End Property Quando um item na combo for selecionado, habilitamos o botão ok. Quando o botão Ok for clicado, atribuimos a classe selecionada na variável Configuracao. Veja como fica : 265 Public Configuracao As config 266 267 Private Sub cmbConfigs_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbConfigs.SelectedIndexChanged 268 cmdOk.Enabled = True 269 End Sub 270 271 Private Sub cmdOk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOk.Click 272 Me.Configuracao = DirectCast(cmbConfigs.SelectedItem, config) 273 End Sub Quando o usuário clicar no botão para adicionar uma nova configuração precisaremos fazer os seguintes passos :
Veja como fica : 59 Dim nomeConfig As String 60 nomeConfig = InputBox("Informe o nome da nova configuração", "Nova Configuração") 61 62 If nomeConfig = "" Then 63 Exit Sub 64 End If 65 66 Dim dataLink As Object = Microsoft.VisualBasic.Interaction.CreateObject("DataLinks") 67 dataLink.hWnd = Me.Handle 68 69 Dim o As Object = dataLink.PromptNew() 70 If o Is Nothing Then 71 Exit Sub 72 Else 73 Dim cl As New config(nomeConfig, o.connectionstring.ToString, hs.Count) 74 hs.Add(cl) 75 Dim c As CurrencyManager 76 c = Me.BindingContext(hs) 77 c.SuspendBinding() 78 c.ResumeBinding() 79 80 End If Observe que neste código utilizei um truque para exibir para o usuário a própria janela de configuração de uma conexão do OLEDB, permitindo desta forma que o usuário crie a string de conexão em um ambiente já familiar a ele. Isso foi exposto em uma dica em http://www.bufaloinfo.com.br/dicas.asp?cod=430
Agora que chegamos neste ponto e a aplicação já está funcionando e guardando configurações diversas do usuário, vamos então inserir na aplicação a capacidade de fazer filtragens das informações exibidas. Vamos criar 2 tipos de filtragem : Uma filtragem simples, aplicada sobre os dados exibidos e uma filtragem relacionada, por exemplo, o usuário desejará ver os campos de uma determinada tabela. Vamos começar, claro, pela filtragem mais simples. Precisaremos criar um formulário novo através do qual o usuário possa definir a filtragem a ser realizada. Vamos chamar este novo formulário de frmRestricoes. Muitos objetos deste formulário deverão ser dinâmicos. Ele deverá exibir os campos existentes na DataTable que será filtrada (exibir mesmo campos que tenham sido excluidos pelo usuário) e permitir que o usuário digite, para cada campo, o critério de filtro, ou deixe o campo vazio. Assim sendo deverão ser criados labels e textboxes tantos quantos forem os campos da dataTable sendo filtrada. Mas isso se complica um pouco mais. As DataTables possuem os campos devolvidos pelo provider OLEDB, fornecendo informações sobre determinados tipos de objetos no banco. O problema é que alguns dos campos em cada DataTable são específicos de determinados data providers e, por isso, não podem ser usados como parte das restrições. As DataTables tem um conjunto de campos genéricos, estes podendo ser usados nas restrições, e um conjunto de campos específicos, que não podem. O número de campos que podem ou não serem utilizados nas restrições variam para cada DataTable. No endereço http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbschema_rowsets.asp encontrei documentação sobre quantas colunas de cada tipo de DataTable podem ser usadas como critério. Isso acaba sendo algo fixo, veja como codifiquei, para resolver o problema : 292 Private Sub DefinirRestricoes() 293 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Assertions, 3) 294 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Catalogs, 1) 295 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Character_Sets, 3) 296 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Check_Constraints, 3) 297 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Check_Constraints_By_Table, 6) 298 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Collations, 3) 299 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Column_Domain_Usage, 3) 300 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Column_Privileges, 6) 301 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Columns, 4) 302 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Constraint_Column_Usage, 7) 303 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Constraint_Table_Usage, 5) 304 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Foreign_Keys, 6) 305 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Indexes, 5) 306 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Key_Column_Usage, 7) 307 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Primary_Keys, 3) 308 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Procedure_Columns, 4) 309 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Procedure_Parameters, 4) 310 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Procedures, 4) 311 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Provider_Types, 2) 312 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Referential_Constraints, 3) 313 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Schemata, 3) 314 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Statistics, 3) 315 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Table_Constraints, 7) 316 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Table_Privileges, 5) 317 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Table_Statistics, 7) 318 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Tables, 4) 319 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Tables_Info, 4) 320 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Translations, 3) 321 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Usage_Privileges, 6) 322 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.View_Column_Usage, 3) 323 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.View_Table_Usage, 3) 324 hsNumRestricoes.Add(System.Data.OleDb.OleDbSchemaGuid.Views, 3) 325 End Sub |