C# - Acessar um arquivo DBF e copiar para o SQL Server


Neste artigo vamos recordar como acessar um arquivo no formato DBF e copiar o seu conteúdo para um banco de dados SQL Server usando a linguagem C#.

Em pleno século XXI quem é que precisaria acessar um arquivo DBF ?

Pode estar certo que se você procurar vai encontrar muitos aplicativos rodando que ainda acessam arquivos DBF, e, ola que não serão poucos.

A resposta pode ser surpreendente e os motivos também mas vamos deixar essa discussão de lado e passar para a parte prática.

E agora, oque fazer se você precidar acessar, ler e exportar o contéudo de um DBF ? Por onde começar ???

Bem, você poderia usar o DBase, FoxPro , um utilitário de terceiros, etc., etc. Mas neste artigo vamos usar apenas código C#.

Para escrever reste artigo eu coletei informações de diversos sites da Web cujas referências estão no final do artigo.

A seguir vou elencar as ferramentas que foram usadas :

Gostaria de avisar que o acesso foi feito em um arquivo no formato DBF do FoxPro, para outros formatos eu não testei e não garanto que o código vai funcionar.

A figura abaixo mostra as etapas que iremos realizar :

Chega de papo e vamos ao trabalho...

Selecionando os arquivos DBF de exemplo

Acesse o link https://github.com/infused/dbf/tree/master/spec/fixtures e baixe os arquivos :

E copie os arquivos para uma mesma pasta no seu computador. Neste artigo esses arquivos estarão na pasta c:\dados\dbf.

Definindo o banco de dados SQL Server

Neste artigo eu vou criar uma tabela em um banco de dados SQL Server para armazenar os dados do arquivo DBF. Para tornar o código mais simples eu vou escolhar um banco de dados SQL Server existente chamado CadastroDB.mdf para armazenar a tabela.

Você pode escolher qualquer outro banco de dados, e, pode ainda criar via código o banco de dados.

A string de conexão do banco de dados CadastroDB.mdf usada neste artigo é a seguinte :
"Data Source=.;Initial Catalog=CadastroDB;Integrated Security=SSPI";

Criando um projeto Windows Forms no VS 2017 Community

Criar um novo projeto Windows Desktop usando o template Windows Forms App(.NET Framework)  usando o VS 2017 Community com o nome WF_LeSalva_Dbf;

A partir da ToolBox inclua no formulário Form1.cs os controles:

Disponha os controles conforme o leiaute da figura abaixo:

Antes de prosseguir não esqueça de instalar o driver do provedor Visual FoxPro OLE DB Provider.

Definindo o código do formulário Form1.cs:

Namespaces usados

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Windows.Forms;

A seguir vamos definir variáveis que iremos usar no formulário :

DataTable dtData, dtColumn;
string nomeArquivoDbf = @"c:\dados\dbf\contacts.dbf";
string conString = "Data Source=.;Initial Catalog=CadastroDB;Integrated Security=SSPI";
public IList<DbfFieldDescriptor> FieldDescriptors { get; set; }

No evento Click do botão  - Acessar e Exportar dados do arquivo DBF - inclua o código que vai chamar o método para acessar e ler o arquivo DBF:

        private void btnLerDBF_Click(object sender, EventArgs e)
        {
            try
            {
                LeArquivoDBF();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Erro :" + ex.Message);
            }
        }

 

Código do método LeArquivoDBF

Este método acessa o arquivo DBF e le os seus dados usando o provedor VFPOLEDB1 e define um DataTable em memória para copiar as informações do DBF:

        private void LeArquivoDBF()
        {
            string caminhoArquivoDbf = Path.GetDirectoryName(nomeArquivoDbf);
            OleDbConnection connection = new OleDbConnection("Provider=VFPOLEDB.1;Data Source="
 + caminhoArquivoDbf + ";");

            connection.Open();
            DataTable tables = connection.GetSchema(OleDbMetaDataCollectionNames.Tables);

            dtColumn = null;
            string fName = Path.GetFileNameWithoutExtension(nomeArquivoDbf);

            foreach (DataRow rowTables in tables.Rows)
            {
                if (rowTables["table_name"].ToString().ToUpper() == fName.ToUpper())
                {
                    DataTable columns = connection.GetSchema(OleDbMetaDataCollectionNames.Columns,
                        new String[] { null, null, rowTables["table_name"].ToString(), null });
                    dtColumn = GetColumnDataTable();
                    foreach (System.Data.DataRow rowColumns in columns.Rows)
                    {
                        DataRow dr = dtColumn.NewRow();
                        dr[0] = rowColumns["column_name"].ToString();
                        dr[1] = OleDbType(int.Parse(rowColumns["data_type"].ToString()));
                        dr[2] = rowColumns["data_type"].ToString();
                        dr[3] = rowColumns["numeric_precision"].ToString();
                        dtColumn.Rows.Add(dr);
                    }
                    break;
                }
            }
            string sql = "SELECT * FROM " + arquivoDBF;
            OleDbCommand cmd = new OleDbCommand(sql, connection);
            OleDbDataAdapter oledbAdapter = new OleDbDataAdapter(cmd);
            dtData = new DataTable();
            oledbAdapter.Fill(dtData);
            connection.Close();
            EscreveDadosDatabaseSQLServer(dtData, dtColumn);
    }

O método GetColumnDataTable() é usado para definir as colunas da tabela:

        static DataTable GetColumnDataTable()
        {
            DataTable tabela = new DataTable();
            tabela.Columns.Add("NAME", typeof(string));
            tabela.Columns.Add("TYPE", typeof(string));
            tabela.Columns.Add("TYPENO", typeof(string));
            tabela.Columns.Add("DEC", typeof(string));
            return tabela;
        }

O método OleDbType() é usasdo para identificar o datatype usado no DBF e retornar o equivalente:

        public string OleDbType(int type)
        {
            string dataType;
            switch (type)
            {
                case 10:
                    dataType = "BigInt";
                    break;
                case 128:
                    dataType = "Byte";
                    break;
                case 11:
                    dataType = "Boolean";
                    break;
                case 8:
                    dataType = "String";
                    break;
                case 129:
                    dataType = "String";
                    break;
                case 6:
                    dataType = "Currency";
                    break;
                case 7:
                    dataType = "DateTime";
                    break;
                case 133:
                    dataType = "DateTime";
                    break;
                case 134:
                    dataType = "TimeSpan";
                    break;
                case 135:
                    dataType = "DateTime";
                    break;
                case 14:
                    dataType = "Decimal";
                    break;
                case 5:
                    dataType = "Double";
                    break;
                case 3:
                    dataType = "Integer";
                    break;
                case 201:
                    dataType = "String";
                    break;
                case 203:
                    dataType = "String";
                    break;
                case 204:
                    dataType = "Byte";
                    break;
                case 200:
                    dataType = "String";
                    break;
                case 139:
                    dataType = "Decimal";
                    break;
                case 202:
                    dataType = "String";
                    break;
                case 130:
                    dataType = "String";
                    break;
                case 131:
                    dataType = "Decimal";
                    break;
                case 64:
                    dataType = "DateTime";
                    break;
                default:
                    dataType = "";
                    break;
            }
            return dataType;
        }

Código do método EscreveDadosDataBaseSQLServer

Ao final após ler os dados do DBF e preencher o DataTable em memória o método EscreveDadosDatabaseSQLServer() é chamado.

        public void EscreveDadosDatabaseSQLServer(DataTable dtData, DataTable dtCol)
        {
            string nomeTabela = Path.GetFileNameWithoutExtension(nomeArquivoDbf);
            string caminhoArquivo = nomeArquivoDbf;
            SqlConnection dbCon = new SqlConnection(conString);

            if (dbCon.State == ConnectionState.Closed)
                dbCon.Open();

            string strQuery = "IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'" + nomeTabela + "')) BEGIN SELECT 1 END ELSE BEGIN SELECT 0 END";

            SqlCommand dbCmd = new SqlCommand(strQuery, dbCon);
            SqlDataAdapter dbDa = new SqlDataAdapter(dbCmd);
            DataTable tabelaExiste = new DataTable();
            dbDa.Fill(tabelaExiste);

            int valReturn = int.Parse(tabelaExiste.Rows[0][0].ToString());
            if (valReturn == 0)
            {
                ReadFileStream(caminhoArquivo, dtCol);
                CriaDataTable(nomeTabela);
            }
            //salva os dados na tabela do banco de dados
            SalvaDadosNoDatabaseSqlServer(dtData, nomeTabela, dtCol);
        }

Este código verifica se a tabela com o mesmo nome do DBF existe e a seguir chama o método ReadFileStream que vai definir os descritores dos campos.   

       void ReadFileStream(string caminhoArquivo, DataTable dtCol)
        {
            FileStream fileStream = new FileStream(caminhoArquivo, FileMode.Open, FileAccess.Read, FileShare.Read);
            BinaryReader binaryReader = new BinaryReader(fileStream);

            var descritoresDeCampos = new List<DbfFieldDescriptor>();
            try
            {
                int no = 0;
                while (true)
                {
                    var descritorCampo = ReadFieldDescriptor(binaryReader, no++, dtCol);
                    if (descritorCampo == null)
                        break;
                    if (no > 1)
                        descritoresDeCampos.Add(descritorCampo);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Falhou ao ler os descritores de campos...", e);
            }
            FieldDescriptors = descritoresDeCampos;
        }

Cada descritor de campo para uma coluna é obtido via método ReadFieldDescriptor() com o código a seguir:

 DbfFieldDescriptor ReadFieldDescriptor(BinaryReader br, int fdNo, DataTable dtCol)
        {
            var campoDescritor = new DbfFieldDescriptor();
            campoDescritor.No = fdNo;
            string nome = "";
            if (fdNo > 0 && fdNo <= dtCol.Rows.Count)
                nome = dtCol.Rows[fdNo - 1][0].ToString();
            try
            {
                var fieldNameBytes = new byte[11];
                fieldNameBytes[0] = br.ReadByte();
                if (fieldNameBytes[0] == 0x0D)
                    return null; // 0x0D significa fim da lista de campo descritor
                br.Read(fieldNameBytes, 1, 10);
                campoDescritor.Name = nome;
                campoDescritor.TypeChar = (char)br.ReadByte();
                br.ReadByte(); // reservado  
                br.ReadByte(); // reservado  
                br.ReadByte(); // reservado  
                br.ReadByte(); // reservado  
                campoDescritor.Length = br.ReadByte();
                campoDescritor.DecimalCount = br.ReadByte();
                br.ReadBytes(2); 
                br.ReadByte(); 
                br.ReadBytes(10); 
                br.ReadByte();    
                return campoDescritor;
            }
            catch (Exception e)
            {
                if (string.IsNullOrWhiteSpace(campoDescritor.Name))
                    throw new Exception($"Falha ao ler o campo descritor #{fdNo + 1}", e);
                else
                    throw new Exception($"Falha ao ler o campo descritor #{fdNo + 1} ({campoDescritor.Name})", e);
            }
        }

Para obter os descritores dos campos para as colunas vamos criar uma classe chamada DbfFieldDescriptor com o código abaixo:

using System;
namespace WF_LeSalva_Dbf
{
    public class DbfFieldDescriptor
    {
        public int No { get; set; }
        public string Name { get; set; }
        public char TypeChar { get; set; }
        public int Length { get; set; }
        public byte DecimalCount { get; set; }
        public string GetSqlDataType()
        {
            switch (TypeChar)
            {
                case 'C':
                    return $"VARCHAR({Length})";
                case 'I':
                    return "INT";
                case 'N':
                    return $"DECIMAL({Length + 1}, {DecimalCount})";
                case 'L':
                    return "BIT";
                case 'B':
                    return "BIT";
                case 'D':
                    return "DATETIME";
                case 'M':
                    return "VARCHAR(MAX)";
                case 'S':
                    return "VARCHAR(MAX)";
                case 'T':
                    return "DATETIME";
                case 'W': //?  
                    return "VARCHAR(MAX)";
                case '0':
                    return "INT";
                case 'G':
                    return "VARCHAR(MAX)";
                case 'F':
                    return "FLOAT";
                case 'Y':
                    return "NUMERIC(18,4)";
                default:
                    throw new NotSupportedException();
            }
        }
    }
}

Código do método SalvaDadosNoDatabaseSqlServer

Este método salva os dados na tabela criada no banco de dados e exibe os dados no DataGridView:

 // salva os registros na tabela do banco de dados
        public void SalvaDadosNoDatabaseSqlServer(DataTable dtTabela, string nomeTabela, DataTable dtColumn)
        {
            if (dtTabela.Rows.Count > 0)
            {
                SqlConnection dbCon = new SqlConnection(conString);
                for (int n = 0; n < dtTabela.Rows.Count; n++)
                {
                    if (dbCon.State == ConnectionState.Closed)
                        dbCon.Open();
                    string strSql = "";
                    strSql = "INSERT INTO [" + nomeTabela + "] VALUES(";
                    for (int i = 0; i < dtColumn.Rows.Count; i++)
                    {
                        if (i == dtColumn.Rows.Count - 1)
                        {
                            if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "boolean" || 
dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "logical")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'False','true')";
                                else
                                    strSql = strSql + "'" + dtTabela.Rows[n][i].ToString() + "','true')";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "string")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'','true')";
                                else
                                    strSql = strSql + "'" + dtTabela.Rows[n][i].ToString().Replace("'", "") + "','true')";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "byte")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'0','true')";
                                else
                                    strSql = strSql + "'" + Encoding.ASCII.GetBytes(dtTabela.Rows[n][i].ToString()) + "','true')";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "character")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'','true')";
                                else
                                    strSql = strSql + "'" + dtTabela.Rows[n][i].ToString().Replace("'", "") + "','true')";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "datetime" || 
dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "date")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "null,'true')";
                                else
                                    strSql = strSql + "'" + DateTime.Parse(dtTabela.Rows[n][i].ToString()) + "','true')";
                            }
                            else
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "0,'true')";
                                else
                                    strSql = strSql + dtTabela.Rows[n][i].ToString() + ",'true')";
                            }
                        }
                        else
                        {
                            if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "boolean" || 
dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "logical")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'False',";
                                else
                                    strSql = strSql + "'" + dtTabela.Rows[n][i].ToString() + "',";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "string")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'',";
                                else
                                    strSql = strSql + "'" + dtTabela.Rows[n][i].ToString().Replace("'", "") + "',";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "byte")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'0',";
                                else
                                    strSql = strSql + "'" + Encoding.ASCII.GetBytes(dtTabela.Rows[n][i].ToString()) + "',";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "character")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "'',";
                                else
                                    strSql = strSql + "'" + dtTabela.Rows[n][i].ToString().Replace("'", "") + "',";
                            }
                            else if (dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "datetime" || 
dtColumn.Rows[i]["TYPE"].ToString().ToLower() == "date")
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "null,";
                                else
                                    strSql = strSql + "'" + DateTime.Parse(dtTabela.Rows[n][i].ToString()) + "',";
                            }
                            else
                            {
                                if (string.IsNullOrWhiteSpace(dtTabela.Rows[n][i].ToString()))
                                    strSql = strSql + "0,";
                                else
                                    strSql = strSql + dtTabela.Rows[n][i].ToString() + ",";
                            }
                        }
                    }
                    try
                    {
                        SqlCommand dbCmd1 = new SqlCommand(strSql, dbCon);
                        dbCmd1.ExecuteNonQuery();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Error " + ex.Message);
                    }
                }
                dbCon.Close();
                dgvDados.DataSource = dtTabela;
            }
        }

Executando o projeto, para o arquivo DBF em questão iremos obter o seguinte resultado :

Verificando o banco de dados CadastroDB.mdf veremos a tabela contacts criada conforme mostrado abaixo:

Pegue o código do projeto aqui :    WF_LeSalva_Dbf.zip

"O Senhor é o meu rochedo, e o meu lugar forte, e o meu libertador; o meu Deus, a minha fortaleza, em quem confio; o meu escudo, a força da minha salvação, e o meu alto refúgio."
Salmos 18:2

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti