2

Conectando e consultando planilhas Excel com C#

by Luiz Jr 8. dezembro 2011 22:40

Excel

O post de hoje é algo que muitos desenvolvedores não fazem nem idéia que é possível: conectar e utilizar planilhas Excel como se fosse um banco de dados, e não estou falando de CSV. Sim, isso mesmo, ao invés de ficar armazenando em flat-files (os populares TXT com marcações) você pode utilizar o Excel para trabalhar como se fosse um autêntico banco de dados...Ok, eu é que não vou ficar fazendo apologia ao uso de Excel ao invés de um SGBD de verdade. O real intuito deste post é ensinar como consumir os dados existentes em uma planilha de terceiros. Muitas vezes há a necessidade de utilizar dados de planilhas legadas para importar em sua base de verdade, ou então você pode precisar desenvolver um sistema que agregue dados de planilhas diferentes em um único banco e por aí vai.

A Conexão

Obviamente, o primeiro passo é se conectar na dita planilha. E é aqui onde a maioria dos desenvolvedores se quebra pois esta etapa possui algumas particuliaridades. Em primeiro lugar, como toda conexão com base de dados você precisará utilizar classes do ADO.NET, o framework de acesso a dados da plataforma .NET. No caso do Excel, a melhor opção é utilizarmos uma conexão OLE DB. Para quem não sabe, OLE DB (Object Linking and Embedding DataBase) é uma API desenvolvida pela Microsoft para acesso a dados de forma nativa no Windows, via COM. Pois bem, através de OLE DB, podemos acessar dados em Access e Excel. Quando o assunto é Access, a melhor alternativa é utilizar ODBC, mas com Excel, OLEDB é uma boa opção. Como de praxe, você precisará importar a biblioteca System.Data e instanciar um OleDbConnection conforme mostrado abaixo:

using System.Data;
var _conexao = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=planilha.xls;Extended Properties='Excel 8.0;HDR=YES;'");
_conexao.Open();

A string de conexão determina o provedor a ser utilizado na conexão (no meu exemplo, a API OLE DB versão 4.0, nativa do Windows), a fonte dos dados (basicamente o caminho completo até a planilha, no meu exemplo, ela se encontra na mesma pasta do programa) e algumas propriedades específicas da conexão, como a versão do Excel a (8.0) e se a planilha possui cabeçalho (HDR = Header = Cabeçalho). A string de conexão OLE DB é muito sensível, então muita atenção à sua escrita, erros comuns incluem escrever 'DataSource' ao invés de 'Data Source', ou então usar uma versão de Excel errada. Outro erro muito comum é colocar a propriedade de cabeçalho fora da string 'Extended Properties' (note que a versão do Excel e a informaçãod e cabeçalhos existentes estão entre aspas). Qualquer erro em sua string de conexão irá gerar o erro "Não foi possível encontrar ISAM instalável." que é algo extremamente genérico e sem muita utilidade.

 

A Consulta

Uma vez que a consulta foi instanciada, agora podemos executar comandos sobre a planilha à qual nos conectamos. Você já deve ter utilizado mais de uma planilha em uma mesma pasta do Excel, não é mesmo? E como você fazia para se organizar? Colocava nomes nas planilhas, certo? É isso mesmo! Imagine a sua pasta do Excel como seu "banco de dados" e cada planilha dentro da pasta como suas "tabelas". E como você faria uma consulta no SQL Server sobre uma tabela do seu banco? 'SELECT * FROM Tabela'? E é isso mesmo que você vai fazer! Dê uma olhada no código abaixo, onde usamos o OleDbCommand para realizar consultas no banco:

var cmd = new OleDbCommand("SELECT * FROM [tabela$]", _conexao);
var dt = new DataTable();
dt.Load(cmd.ExecuteReader());

O código acima é auto-explicativo, é simplesmente a instanciação de um OleDbCommand passando um comando pseudo-SQL (na verdade o nome disso é MS Query) e a conexão instanciada no passo anterior. As linhas seguintes já são corriqueiras a todo programador que já tenha utilizado ADO.NET para se conectar a um banco de dados, a consulta é executada retornando um DataReader, que por sua vez é carregado em um DataTable, para posterior utilização. Preste apenas atenção no fato de que a "tabela" do Excel é delimitada entre colchetes e SEMPRE deve terminar com um cifrão. Não me pergunte o porquê, eu realmente não sei, hehehehe.

Sistema de Exemplo

Logo abaixo você encontra um link para download de um sistema de exemplo, onde criei uma classe Excel, que encapsula toda a lógica de acesso à planilhas, abstraindo do desenvolvedor a necessidade de conhecer detalhes específicos de sua implementação. Use do jeito que quiser, afinal este código eu montei com base em outros da Internet mesmo, hehehehe. Neste mesmo ZIP você encontrará uma planilha de exemplo para usar em seus testes (algum gamer de plantão reconhece os dados da planilha?) e um sisteminha desktop feito em .NET 4 que permite a você conectar em sua planilha preferida e realizar consultas sobre ela, vendo os resultados em um DataGrid. A idéia não era criar um "Excel Management Studio" ou algo do gênero, por isso a aplicação não é laáááá muito útil, mas serve para seu propósito que é ilustrar a utilização de Excel como base de dados e completar o post. Espero que seja útil!

ExcelConnection.zip (50,33 kb)

Tags: , , ,

BD | Dica

11

Criando uma app Android com banco de dados SQLite

by Luiz Jr 20. novembro 2011 23:34

Android + SQLite

Hoje o post irá tratar de um assunto muito importante: persistência de dados. Todas as aplicações que lidam com um grande volume de dados devem ter algum mecanismo de persistência, mais conhecido como banco de dados, para garantir a segurança, confiabilidade e integridade dos mesmos. O Android possui um mecanismo nativo, chamado de SQLite (o mesmo utilizado pelo Corona SDK) que provê funcionalidades de SGBD para suas aplicações. Iremos ver algumas dessas funcionalidades neste post, de uma maneira bem simples, mas que servirá como base para estudos futuros.

Armazenamento persistente de dados

Para cumprir com muitas das atividades oferecidas pelos celulares modernos, como busca de contatos, eventos, e tarefas, o sistema operacional e as aplicações estar aptos a manter e rastrear grandes quantidades de dados. A maior parte destes dados está estruturado como uma planilha, na forma de linhas e colunas. Cada aplicação Android é como uma ilha por si só, e cada aplicação somente consegue ler e escrever dados criados por ela mesma, mas muitas vezes é necesário compartilhar dados além de suas fronteiras. Android suporta características de um provedor de conteúdo para que as aplicações possam compartilhar dados. Existe uma interface Java para que o Android possa se comunicar com a base de dados relacional SQLite. Esta interface suporta uma implementação de SQL rica o suficiente para qualquer coisa que você queira em uma aplicação mobile, incluindo facilidades como cursores.

Bases de Dados

Dados são melhores armazenados em um formato de base de dados relacional se ela puder incluir muitas instâncias de um mesmo tipo de dado. pegue uma lista de contatos, por exemplo. Existem muitos contatos, todos com os mesmos tipos de informação (endereço, telefone, etc). Cada "linha" de dados armazena informações sobre uma pessoa diferente, enquanto cada "coluna" armazena um atributo específico de cada pessoa: nomes em uma coluna, endereços em outra coluna, e telefone em uma terceira.

Android usa a engine de base de dados SQLite, uma auto-contida, engine transacional que não requer um processo de servidor para funcionar. Ele é usado por muitas aplicações e ambientes além de Android, e é desenvolvido por uma grande comunidade.

O processo que inicia a operação de base de dados, como um SELECT ou UPDATE, faz o trabalho necessário de leitura e escrita no disco que contém a base de dados visando completar a requisição. Com SQLite, a base de dados é um simples arquivo no disco. Todas as estruturas de dados formando uma base de dados relacional - tabelas, views, indíces, etc. - estão contidas neste arquivo.

SQLite não é um projeto do Google, embora o Google contribua com o mesmo. SQLite tem um time internacional de desenvolvedores dedicados a melhorar as capacidades do software e sua confiabilidade. Alguns desses desenvolvedores trabalham full time no projeto.

Confiança é a característica chave do SQLite. Mais de metade do código do projeto é devotado a biblioteca de testes. A biblioteca é desenvolvida para lidar com muitos tipos de falhas de sistema, como pouca memória, erros de disco, e falhas de energia. Em nenhum caso a base de dados pode ficar em um estado irreparável: isto é uma grande preocupação em um telefone onde muitos dados críticos são armazenados em bases de dados. Se a base de dados fosse suscetível a corrupção de arquivos, o telefone estaria em sérios apuros quando a bateria acabasse...

Este post irá tratar dos percalços para se criar uma aplicação muito simples que consuma dados no SQLite e não se atrá à sintaxe SQL propriamente. Um pouco de Engenharia de Software será utilizada, mantendo toda a lógica de acesso à dados em uma única classe chamada ContextoDados.java como segue (ela ainda não está completa, iremos construindo-a ao longo do post):

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteCursorDriver;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQuery;
import android.util.Log;

public class ContextoDados extends SQLiteOpenHelper {

	/** O nome do arquivo de base de dados no sistema de arquivos */
	private static final String NOME_BD = "Agenda";
	/** A versão da base de dados que esta classe compreende. */
	private static final int VERSAO_BD = 1;
	private static final String LOG_TAG = "Agenda";
	/** Mantém rastreamento do contexto que nós podemos carregar SQL */
	private final Context contexto;
	
	public ContextoDados(Context context) {
		super(context, NOME_BD, null, VERSAO_BD);
		this.contexto = context;
		}

	@Override
	public void onCreate(SQLiteDatabase db) 
	{
		String[] sql = contexto.getString(R.string.ContextoDados_onCreate).split("\n");
		db.beginTransaction();
		
		try 
		{
			// Cria a tabela e testa os dados
			ExecutarComandosSQL(db, sql);
			db.setTransactionSuccessful();
		} 
		catch (SQLException e) 
		{
			Log.e("Erro ao criar as tabelas e testar os dados", e.toString());
		} 
		finally 
		{
			db.endTransaction();
		}
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
	{
		Log.w(LOG_TAG, "Atualizando a base de dados da versão " + oldVersion + " para " + newVersion + ", que destruirá todos os dados antigos");
		String[] sql = contexto.getString(R.string.ContextoDados_onUpgrade).split("\n");
		db.beginTransaction();
		
		try 
		{
			ExecutarComandosSQL(db, sql);
			db.setTransactionSuccessful();
		}
		catch (SQLException e) 
		{
			Log.e("Erro ao atualizar as tabelas e testar os dados", e.toString());
			throw e;
		} 
		finally 
		{
			db.endTransaction();
		}
		
		// Isto é apenas didático. Na vida real, você terá de adicionar novas colunas e não apenas recriar o mesmo banco
		onCreate(db);
	}
	
	/**
	* Executa todos os comandos SQL passados no vetor String[]
	* @param db A base de dados onde os comandos serão executados
	* @param sql Um vetor de comandos SQL a serem executados
	*/
	private void ExecutarComandosSQL(SQLiteDatabase db, String[] sql)
	{
		for( String s : sql )
			if (s.trim().length()>0)	
				db.execSQL(s);
	}
}

Estrutura Básica da classe ContextoDados

Em nosso exemplo, a classe ContextoDados encapsula completamente toda a lógica SQL necessária para trabalhar com a base de dados. Todas as outras classes na aplicação não possuem acesso aos dados diretamente, usando ContextoDados como uma interface de abstração dos mesmos. Esta é uma boa prática de programação e deve ser aplicada em todas suas aplicações Android que utilizem base de dados.

Antes de aprofundarmo-nos nos detalhes da aplicação em si, é importante entender o funcionamento da classe ContextoDados. Esta classe herda de SQLiteOpenHelper, e sobrescreve (override) os métodos onCreate e onUpgrade. O método onCreate é automaticamente chamado quando a aplicação roda pela primeira vez; sua tarefa é criar a base de dados. Como novas versões daaplicação podem ser lançadas, a base de dados pode ser atualizada também, uma tarefa que dispara o método onUpgrade. Quando você entrega uma nova versão da base de dados, você também deve incrementar a versão, como irei explicar rapidamente a seguir.

E por fim, o método ExecutarComandosSQL é um método para facilitar a execução de comandos SQL múltiplos, que apenas chama os comandos nativos do Android para execução de comandos SQL.

A classe ContextoDados utiliza duas strings estáticas (que ficam no arquivo strings.xml): R.string.ContextoDados_onCreate e R.string.ContextoDados_onUpgrade com os scripts de criação e atualização do banco de dados. Estas strings estão abaixo e não passam de um SQL comum, quevocê já deve estar acostumado:

<string name="ContextoDados_onCreate">
        CREATE TABLE Contatos (ID INTEGER PRIMARY KEY AUTOINCREMENT, Nome TEXT,    Telefone TEXT, Endereco TEXT);
</string>
<string name="ContextoDados_onUpgrade">"
        DROP TABLE IF EXISTS Contatos
</string>

Lendo dados da base

Existem muitas maneiras de ler dados de uma base SQL, mas todos eles fazem uma sequência básica de operações:

  1. Cria um comando SQL que descreve os dados que você deseja retornar
  2. Executa o comando na base de dados
  3. Mapeia os dados SQL resultantes em uma estrutura de dados que a linguagem que você está utilizando possa entender.

Este processo pode ser muito complexo no caso de um software de mapeamento objeto relacional, ou relativamente simples quando escrevendo as consultas diretamente em sua aplicação. A diferença é a fragilidade. Ferramentas complexas de ORM protegem seu código das complexidades inerentes às bases de dados e o mapeamento de objetos, movendo esta complexidade para eles mesmos. O resultado é um código mais robusto face às alterações na base, mas ao custo de mais complexidade na configuração e manutenção do ORM.

A iniciativa de escrever consultas diretamente na sua aplicação trabalha bem somente para projetos bem pequenos que não irão mudar muito com o passar do tempo. Aplicações com código de base de dados são muito frágeis devido às alterações da mesma, pois todo o código que referenciava um elemento alterado deve ser examinado e potencialmente reescrito.

Uma técnica "meio-termo" é capturar toda a lógica de base de dados em um grupo de objetos cujo único propósito é traduzir as requisições da aplicação em requisições de banco de dados e entregar os resultados de volta para a aplicação. Esta opção é a utilizada em nossa aplicação de teste utilizando a classe ContextoDados.java.

Android nos dá a habilidade de personalizar cursores, e eu usarei essa característica para reduzir dependências de código escondendo toda a informação sobre uma operação de dados específica dentro d eum cursor personalizado. Cada cursor personalizado é uma classe dentro da classe ContextoDados, no nosso caso, somente o ContatosCursor.

Desta forma, criei um método RetornarContatos, cuja função é retornar um ContatosCursor preenchido com contatos da base de dados. O usuário pode escolher (através de um simples parâmetro) para ordenar os contatos por Nome crescente ou decrescente (este código vai dentro da classe ContextoDados criada anteriormente):

 

/** Retorna um ContatosCursor ordenado
	* @param critério de ordenação
	*/
	public ContatosCursor RetornarContatos(ContatosCursor.OrdenarPor ordenarPor) 
	{
		String sql = ContatosCursor.CONSULTA + (ordenarPor == ContatosCursor.OrdenarPor.NomeCrescente ? "ASC" : "DESC");
		SQLiteDatabase bd = getReadableDatabase();
		ContatosCursor cc = (ContatosCursor) bd.rawQueryWithFactory(new ContatosCursor.Factory(), sql, null, null);
		cc.moveToFirst();
		return cc;
	}
	
	public static class ContatosCursor extends SQLiteCursor
	{
		public static enum OrdenarPor{
			NomeCrescente,
			NomeDecrescente
		}
		
		private static final String CONSULTA = "SELECT Contatos.ID, Nome, Endereco FROM Contatos ORDER BY Nome ";
		
		private ContatosCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable, SQLiteQuery query) 
		{
			super(db, driver, editTable, query);
		}
		
		private static class Factory implements SQLiteDatabase.CursorFactory
		{
			@Override
			public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable, SQLiteQuery query) 
			{
				return new ContatosCursor(db, driver, editTable, query);
			}
		}
		
		public long getID()
		{
			return getLong(getColumnIndexOrThrow("Contatos.ID"));
		}
		
		public String getNome()
		{
			return getString(getColumnIndexOrThrow("Nome"));
		}
		
		public String getEndereco() 
		{
			return getString(getColumnIndexOrThrow("Endereco"));
		}
	}

Tela de listagem

Como o foco deste post não é a criação de um layout ideal para exibição de dados, criei um bem simples que utiliza um TextView para mostrar os dados do banco concatenando strings. O código XML abaixo mostra como deve ser codificado o layout main.xml e a imagem mostra como ele deve se parecer:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/contatos"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/listaContatos"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Nenhum contato cadastrado." />

    <Button
        android:id="@+id/btnNovo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Novo Cadastro" />
   
</LinearLayout>

E o código Java a seguir mostra o início da codificação da Activity principal, que mantém o nome padrão de MainActivity.java (este código vai dentro do corpo da classe MainActivity):

 

Button btnSalvar, btnCancelar, btnNovo;
EditText txtNome, txtEndereco, txtTelefone;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CarregarInterfaceListagem();
    }
    
    public void CarregarInterfaceListagem()
    {
    	setContentView(R.layout.main);
        
        //configurando o botão de criar novo cadastro
        btnNovo = (Button)findViewById(R.id.btnNovo);
        btnNovo.setOnClickListener(new OnClickListener(){
			public void onClick(View v) {
				CarregarInterfaceCadastro();
			}});
        
        CarregarLista(this);
    }

Neste trecho de código podemos ver as chamadas aos métodos CarregarLista (mostrado abaixo) e mais a seguir implementaremos o CarregarInterfaceCadastro:

 

public void CarregarLista(Context c)
    {
    	ContextoDados db = new ContextoDados(c);
        ContatosCursor cursor = db.RetornarContatos(ContatosCursor.OrdenarPor.NomeCrescente);
        
        for( int i=0; i < cursor.getCount(); i++)
        {
        	cursor.moveToPosition(i);
        	ImprimirLinha(cursor.getNome(), cursor.getTelefone());
        }
    }
    
    public void ImprimirLinha(String nome, String telefone)
    {
    	TextView tv = (TextView)findViewById(R.id.listaContatos);
    	
    	if(tv.getText().toString().equalsIgnoreCase("Nenhum contato cadastrado."))
    		tv.setText("");
    	
    	tv.setText(tv.getText() + "\r\n" + nome + " - " + telefone);
    }

Este código nada mais faz do que concatenar strings em um TextView para simular uma listagem de dados vindos do banco. Com o código que vimos até agora, já é possível listar os dados que já estão no banco, mas provavelmente você irá querer escrever também, que veremos na próxima parte!

Escrevendo no Banco de Dados

Voltando ao ContextoDados, o trecho de código a seguir insere um novo registro no banco de Contatos e serve como exemplo para futuras inserções em outras tabelas (este código vai dentro da classe ContextoDados):

 

public long InserirContato(String nome, String telefone, String endereco)
	{
		SQLiteDatabase db = getReadableDatabase();
		
		try
		{
			ContentValues initialValues = new ContentValues();
			initialValues.put("Nome", nome);
			initialValues.put("Telefone", telefone);
			initialValues.put("Endereco", endereco);
			return db.insert("Contatos", null, initialValues);
		}
		finally
		{
			db.close();
		}
	}

Cadastro.xml - Como deve se parecer

E agora devemos criar o layout para a tela de cadastro, que chamei de cadastro.xml. A seção a seguir mostra o código XML para o layout, e a imagem à direita, como ele deve se parecer se tudo estiver correto:

<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/lblNome"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_x="8dp"
        android:layout_y="9dp"
        android:text="@string/lblNome"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <TextView
        android:id="@+id/lblTelefone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_x="7dp"
        android:layout_y="52dp"
        android:text="@string/lblTelefone"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <TextView
        android:id="@+id/lblEndereco"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_x="8dp"
        android:layout_y="95dp"
        android:text="@string/lblEndereco"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <EditText
        android:id="@+id/txtNome"
        android:layout_width="180dp"
        android:layout_height="40dp"
        android:layout_x="78dp"
        android:layout_y="8dp"
        android:inputType="textPersonName" >

        <requestFocus />
    </EditText>

    <EditText
        android:id="@+id/txtTelefone"
        android:layout_width="180dp"
        android:layout_height="40dp"
        android:layout_x="78dp"
        android:layout_y="50dp"
        android:inputType="phone" />

    <EditText
        android:id="@+id/txtEndereco"
        android:layout_width="180dp"
        android:layout_height="40dp"
        android:layout_x="78dp"
        android:layout_y="93dp"
        android:inputType="textPostalAddress" />

    <Button
        android:id="@+id/btnSalvar"
        android:layout_width="82dp"
        android:layout_height="wrap_content"
        android:layout_x="173dp"
        android:layout_y="142dp"
        android:text="@string/btnSalvar" />

    <Button
        android:id="@+id/btnCancelar"
        android:layout_width="92dp"
        android:layout_height="wrap_content"
        android:layout_x="78dp"
        android:layout_y="141dp"
        android:text="Cancelar" />

</AbsoluteLayout>

Note que neste layout temos alguns EditTexts e dois botões. O botão de cancelar apenas retorna à tela inicial, com a listagem tosca de contatos. Já o botão de salvar, deve invocar o método de inserir novo registro, da classe ContextoDados. O código restante da MainActivity está abaixo, para que você possa copi-colar:

 

public void CarregarInterfaceCadastro()
    {
    	setContentView(R.layout.cadastro);
    	
    	//configurando o botão de cancelar cadastro
        btnCancelar = (Button)findViewById(R.id.btnCancelar);
        btnCancelar.setOnClickListener(new OnClickListener(){
			public void onClick(View v) {
				CarregarInterfaceListagem();
			}});
        
        //configurando o formulário de cadastro
        txtNome = (EditText)findViewById(R.id.txtNome);
        txtEndereco = (EditText)findViewById(R.id.txtEndereco);
        txtTelefone = (EditText)findViewById(R.id.txtTelefone);
        
        //configurando o botão de salvar
        btnSalvar = (Button)findViewById(R.id.btnSalvar);
        btnSalvar.setOnClickListener(new OnClickListener(){
			public void onClick(View v) {
				SalvarCadastro();
			}});
    }
    
    public void SalvarCadastro()
    {
    	ContextoDados db = new ContextoDados(this);
		db.InserirContato(txtNome.getText().toString(), txtTelefone.getText().toString(), txtEndereco.getText().toString());
		setContentView(R.layout.main);
		CarregarLista(this);
    }

 

O código é auto-descritivo, mas caso tenha alguma dúvida, não hesite em perguntar pelos comentários.

Conclusões

Este post mostrou o básico de seleção e inserção de dados, com uma exibição bem tosca, longe de ser a ideal para uma base de dados. Você realizar o download dos fontes completos do projeto no link abaixo. Posts futuros devem cobrir outros aspectos da persistência de dados, como deleção e atualização, bem como listagem profissional dos dados usando ListAdapters. Mas isto fica pra próxima!

Agenda.zip (68,85 kb)

Tags: , , , ,

Android | BD | Mobile

0

Migrando MySql para Sql Server com Entity Framework

by Luiz Jr 26. dezembro 2010 00:03

Download do Código-Fonte

Eu até diria que este é um post extra de Natal, se eu não tivesse acabado de olhar no relógio e visto que já passou da meia-noite. De qualquer forma aqui estou eu com mais um post que espero que seja útil para mais alguém do que eu. Desta vez criei um tutorial para ajudar o pessoal que tem uma base de dados MySql e quer migrá-la para SQL Server, ou vice-versa. Na verdade este post deveria se chamar "migre sua base de dados de um banco para outro" porque o que você vai ver aqui se aplica para Oracle, MySql, PostGre, Sql Server e até mesmo XML (como visto mais explicitamente no post anterior). Veremos como se conectar no MySql usando o fantástico Entity Framework, que apesar de não oferecer nativamente suporte ao famoso banco open-source da Sun, digo, Oracle, se mostra bem compatível quando utilizamos o MySql Connector .NET, desenvolvido em Mono (versão open-source do framework .NET) e disponibilizado gratuitamente. Mas chega de blá, blá, blá...

MySql com .NET?

É isso mesmo. Muitos devem ter lido a introdução e pensado com seus botões: "O que esse cara 'tá falando? MySql com .NET?" e a resposta é: "YES, WE HAVE MYSQL!". Ok, eu nem sou tão fã assim do MySql, mas vamos concordar que é uma excelente notícia saber que temos acesso a um banco 100% free (pelo menos por enquanto) com nossa linguagem de programação favorita (não, não estou falando de VB.NET). Neste mercado de hosting brasileiro onde um banco Sql Server não sai por menos de vinte mangos mensais, é bom saber que podemos dividir nosso banco do Wordpress com outras aplicações de graça, hehehehehe.

Mas nem tudo são flores, afinal MySql não é mantido pela Microsoft e portanto o Visual Studio não oferece suporte nativo ao mesmo. Para solucionar esse problema, a galera da Sun (sim o conector já é velho, da época da Sun) desenvolveu um conector MySql em .NET, usando o framework "pau-pra-toda-obra" Mono, que nada mais é do que uma versão open-source do .NET Framework desenvolvido pela comunidade e recentemente auxiliada pela Microsoft (sim, eu ouvi isso da boca do responsável pelo Mono Brasil no TechEd 2010). Para adquirir seu conector basta usar este link e baixar a versão Windows (cuidado para não baixar os fontes, mas sim o executável que está zipado). Na data deste post a versão mais atual é 6.3.5 que juram de pé junto que está 100% compatível com o Visual Studio 2010. Para quem não sabe, as versões anteriores funcionavam somente no VS2008 e eu fui um dos que tiveram muitos problemas com esta transição...Bom, instale a versão Full/Complete do conector e vamos prosseguir com nosso tutorial.

* OBS: além do conector MySql também vamos utilizar o Visual Studio 2010 (gratuito neste link) e o Sql Server 2008 R2 (gratuito neste link).

Let's Get Started!

Iniciamos o projeto de hoje abrindo nosso VS2010, File, New Project, Visual C#, Console Application. Sim, eu gosto de Consoles Applications, e daí? Vamos dar o nome de MySql2SqlServer pois no meu exemplo quero migrar uma base de dados minha que está em um banco MySql para um novo banco Sql Server. Eu sei que não devia mas vou dizer o motivo: eu não me adaptei com o MySql. Já tenho alguns anos de experiência com SQL Server, desde a versão 2000 e me mostro relutante em trocar de banco. Mais uma de minhas manias. De qualquer forma você pode estar querendo fazer isso pelos seus motivos próprios ou de sua empresa, talvez você queira apenas pegar algus registros de uma base existente e jogar em outra, talvez para fazer o upgrade de banco em uma aplicação legado, whatever.

Agora adicione um novo item em seu projeto, escolhendo na categoria Data, o item ADO.NET Entity Data Model, que será o nosso contexto de dados MySql via Entity Framework. Eu expliquei como funciona (por cima) o EF no último tuto, fora que no Google tem informação à beça, então não me aterei a ele aqui. Coloque o nome de ContextoMySql e manda ver no "Add".

Na tela seguinte, "Entity Data Model Wizard", clique em Next deixando a opção default marcada (Generate from database) e em seguida clique em "New Connection" para criarmos uma conexão com nossa base MySql. A imagem abaixo mostra a janela de configuração da conexão, já preparada para configurar uma base MySql. Se no seu VS2010 não aparecer "MySql Database" na opção "Data Source", clique em "Change" para escolhê-la. Se ainda assim não aparecer, experimente desinstalar o Conector, reiniciar o PC e depois instalar novamente (comigo fiz isso e funcionou). Nesta janela coloque os dados para acesso ao seu banco MySql, teste a conectividade e voltando à tela anterior não esqueça de marcar a segunda opção do RadioButton ("Yes, include sensitive...") para guardar todos os dados da conexão no App.config (é apenas um projeto de teste, não se preocupe com segurança aqui). Next.

Na próxima tela ("Choose your database objects") selecione todas as tabelas de seu banco MySql que deseje migrar, que no meu caso é apenas uma tabela contendo todas as marcas de carros existentes (Fiat, Ford, VW, etc). Finish. Será adicionado o arquivo ContextoMySql.edmx à sua aplicação, bem como referências aos namespaces necessários. Para quem não possui uma base MySql para teste, foi incluído no fonte deste projeto uma pasta SQL contendo o script para criação desse banco MySql, já com a inserção dos dados contendo todas as marcas de carros, basta rodar esse script em um front-end MySql como o excelente SqlYog que ele criará a base, a tabela Marca e irá inserir os dados também. Para testar seu contexto de dados com o MySql, digite o seguinte código dentro do método Main do arquivo Program.cs de sua Console Application:

static void Main(string[] args)
{
     luiztoolsEntities contextoMySql = new luiztoolsEntities();
     foreach (var item in contextoMySql.Marca)
     {
         Console.WriteLine(item.Nome);
     }
}

O código é auto-explicativo e como resultado você deve ver impressa uma lista contendo todas as marcas de carros atualmente comercializados no Brasil (cortesia da tabela FIPE). Note como o Entity Framework abstrai o banco que está sendo utilizado fazendo com que você manipule o MySql da mesma forma como faria com o Sql Server. Não é feitiçaria, é tecnologia!

Com o mapeamento objeto-relacional do banco MySql pronto, o segundo passo é criar o relacionamento com seu banco de destino, que no meu caso é o SQL Server. Note que usarei o mesmo banco Sql Server do tutorial anterior, chamado de Xml2Bd. Para quem não possui tal banco, incluí um script SQL no fonte do projeto para criação do referido banco somente com a tabela Marca. Para quem já possui o banco, apenas exclua as linhas do script que criam a base, deixando somente a criação da tabela Marca e rode no Sql Server Management Studio. Os passos são os mesmos, com a exceção que o Data Source deve ser Sql Server e não MySql e chame este arquivo de ContextoSqlServer. Se tudo der certo, mande compilar e você receberá como prêmio um monte de erros, como mostrados na figura abaixo.

Sim, é isso mesmo: existem nomes duplicados na sua aplicação. Isso acontece quando se adiciona mais de um ADO.NET Entity Data Model à sua aplicação com tabelas de nomes iguais (mesmo que em bancos diferentes). Solução? Basta renomear a classe Marca de um dos arquivos .EDMX. Eu escolhi renomear a classe Marca do arquivo ContextoSqlServer para MarcaSql, clicando sobre a classe e apertando F2. Após salvar o arquivo e compilar novamente, você verá que todos os erros se foram. Mas lembre-se: a classe Marca está associada à tabela Marca do banco MySql e a classe MarcaSql está associada à tabela Marca do banco Sql Server.

Você viu no trecho de código anterior como é fácil listar todas as Marcas de carros existentes no seu banco MySql. Agora, ao invés de imprimir cada um dos resultados da consulta, basta inseri-los no banco de destino, conforme segue abaixo:

static void Main(string[] args)
{
    luiztoolsEntities contextoMySql = new luiztoolsEntities();
    Xml2BdEntities contextoSqlServer = new Xml2BdEntities();

    foreach (var item in contextoMySql.Marca)
    {
        MarcaSql marca = new MarcaSql();
        marca.ID = item.ID;
        marca.Nome = item.Nome;
        marca.Sinonimo = item.Sinonimo;

        contextoSqlServer.AddToMarca(marca);
    }

    contextoSqlServer.SaveChanges();
}

Pronto! Note como é simples manipular dois bancos ao mesmo tempo e até mesmo compartilhar dados entre os dois, mesmo em um ambiente heterogêneo como MySql com Sql Server. Recomendo que baixem os fontes e testem em suas máquinas. Quaisquer dúvidas, estou à disposição.

Conclusão

Espero que este pequeno tutorial tenha sido bom o bastante para mostrar que apesar da dobradinha .NET + SQL Server funcionar perfeitamente, é extremamente viável a utilização de outros bancos de dados junto à plataforma de desenvolvimento da Microsoft. Neste exemplo utilizamos MySql, mas sei de desenvolvedores que estão utilizando conectores Oracle e em meu trabalho já utilizamos o conector de PostGre SQL. Este tutorial também serviu para mostrar como efetuar com sucesso uma tarefa que até pouco tempo atrás era impossível para mim: migrar dados entre bancos heterogêneos através de um script simples e de forma rápida.

Espero que tenham gostado e até a próxima!

Tags: , ,

BD | Entity Framework | SQl Server

6

Migrando XML para SQL Server com Entity Framework

by Luiz Jr 24. dezembro 2010 14:24

Download do Código-Fonte

O objetivo deste post é ajudar os leitores a realizar uma tarefa muito penosa e que por mais de uma vez eu já tive de fazer: migrar bases de dados XML para algum banco de dado Entidade-Relacional, como SQL Server, MySQL, etc. O cenário é o seguinte: por algum motivo você tem um arquivo XML contendo dezenas, centenas, MILHARES de informações muito úteis para seu sistema (como um arquivo contendo todas as cidades brasileiras, por exemplo) mas não-curte/não-quer usar Linq To SQL para manipular o XML como se fosse uma base de dados. O que você quer na verdade é apenas pegar aquelas informações e colocar em seu banco de dados favorito. Cadastrar tudo na mão usando Inserts? 'Tá maluco? Este é o objetivo do post de hoje: como migrar um XML para uma base de dados, como exemplo, o SQL Server.

Ingredientes

  • Nós vamos precisar de um Visual Studio 2010 (pode ser a excelente versão Express distribuída gratuitamente neste link).
  • Também iremos utilizar uma base de dados Entidade-Relacional de sua preferência. Em nosso exemplo utilizaremos o SQL Server 2008 R2 (também possui versão Express gratuita neste link). Se for utilizar outra base como Oracle, Postgre SQL ou MySql, não esqueça de baixar seu conector .NET nos sites dos desenvolvedores desses bancos para que o Visualo Studio reconheça os mesmos.
  • Um arquivo XML contendo muitas informações úteis que você deseja inserir no banco. No exemplo utilizaremos nosso Estados.xml, disponibilizado no post anterior.
  • Muita vontade de ler este blogueiro chato!

Preparativos

Abra seu VS2010 e crie um novo projeto do tipo Console Application. Nada impede que você utilize outro projeto, é apenas mania minha criar Consoles Applications para scripts desse tipo. A receita aqui é velha: File -> New | Project -> Visual C# | Windows | Console Application. Dê o nome que quiser (Xml2Bd no meu caso) e toca ficha na criação. Note que vamos deixar a versão do Framework como 4, conforme o padrão do VS2010. Porque não podemos utilizar na versão 3.5? Duas palavras: Entity Framework. O Entity Framework surgiu como uma feature adicional ainda durante a versão 3.0 do Framework .NET e foi incorporado definitivamente na versão 3.5, porém (sempre tem um porém...) era uma porcaria. Isso mesmo, a versão inicial do EF possui muitos bugs largamente conhecidos entre a comunidade de desenvolvedores como rastreamento de objetos, contexto de objetos, desenvolvimento em N camadas e por aí vai. Boa parte desses bugs foram solucionados na nova versão, presente no Framework 4. Por isso você está fazendo um favor a si mesmo se não perder tempo tentando usar o EF do Framework 3.5, vai por mim.

Este tutorial espera que você tenha os conhecimentos necessários para criar sozinho uma base de dados no SQL Server utilizando o fantástico SQL Server Management Studio (se estiver com preguiça, use o script SQl pronto que deixei dentro da Solution para download no início deste tuto). Você precisará de um banco de dados com o nome de Xml2Bd contendo uma única tabela Estado (o singular é proposital mas não obrigatório) com as seguintes colunas: ID (int, PK), IDCapital (int), Sigla (char[2]) e Nome (nvarchar[50]). Este é o banco onde serão armazenados os dados do XML. Se for fazer este exemplo com o xml de cidades também, não esqueça de adicionar uma tabela Cidade no banco contendo as devidas colunas. A figura abaixo mostra como seu banco deve se parecer para que o script que criaremos a seguir funcione corretamente. Note que não criei o relacionamento das tabelas, pois isso faria com que não conseguíssemos inserir Estados antes de cidades e vice-versa por causa das restrições de Foreign Keys. Também não deixei os IDs das tabelas como auto-incremento pois no próprio XML já está contido os IDs de cada registro.

O próximo passo é adicionar o arquivo XML ao seu projeto. Crie uma pasta XML e jogue lá dentro o dito-cujo, no nosso caso, o Estados.xml. Sem figura para esta etapa, todo mundo está careca de saber que basta clicar o botão direito do mouse sobre o projeto e ir em Add | New Folder para criar a pasta e em Add | Existing Item para adicionar o arquivo na pasta. No passo seguinte (ok, desta vez coloco figura) adicione um novo item ao projeto seguindo o caminho Add | New Item -> Visual C# | Data | ADO.NET Entity Data Model. Coloque o nome de ContextoDados e clique em Add. Este arquivo com a extensão .EDMX nada mais é do que um mapeador objeto-relacional. Só isso. Ok eu explico: esse arquivo .EDMX com o nome de ContextoDados conterá informações sobre o banco de dados que você criou no passo anterior, ele vai mapear cada uma das tabelas que existem lá e criar classes para elas, facilitando o manuseio. Através dos próximos passos você vai entender isso melhor.

Iniciará o Entity Data Model Wizard, um assistente para mapeamento do seu banco. No primeiro passo deixe a opção "Generate from Database" marcada e clique em Next. Na tela seguinte (Choose your data connection), clique em "New Connection" para adicionar uma conexão com seu banco de dados (criado lá no início, lembra?) ou apenas selecione ela no ComboBox se estiver aparecendo (meu caso). Clique em Next.

Em seguida (Choose your database objects) você deve selecionar quais objetos do seu banco você deseja mapear. No nosso caso, selecione apenas a tabela Estado (ou Cidade também, se a incluiu no banco como eu) e mantendo as demais configurações por padrão, clique em Finish. Ao concluir o processo, serão adicionadas algumas referências ao seu projeto, bem como o arquivo EDMX citado anteriormente. O VS2010 também já deixará o dito arquivo aberto para que você visualize o resultado do mapeamento em um diagrama semelhante aos ER tradicionais. Agora você tem o XML com os dados e o banco devidamente mapeado para objetos (cortesia do EF), go code!

Mãos à Obra!

Depois dessa preparação que mais pareceu tarefa de setor de infraestrutura (NNF na gíria deles), vamos finalmente ao que interesse: o código! Basicamente o que faremos aqui será o seguinte: analisaremos o arquivo XML de estados e para cada estado lá presente, instanciaremos uma classe Estado (criada automaticamente pelo EF durante o mapeamento), preencheremos suas propriedades com os valores do XML e salvaremos ela no ContextoDados, que por sua vez salvará no banco. Simples assim. Obviamente isso exige algum conhecimento de Linq To XML, que não será coberto neste post (quem sabe futuramente?) mas que não faltam informações no Tio Google. Sim, eu darei os scripts de Linq To XML, mas seria bom se você realmente estudasse um pouco mais porque é realmente muito poderoso trabalhar com XML desta forma.

Começamos nosso código abrindo o arquivo XML com Linq2Xml e imprimindo todos seus estados no console. Esse código vai dentro do método Main no arquivo Program.cs de sua Console Application. Note que o caminho do arquivo XML deve ser alterado para condizer com a sua realidade. Se tudo der certo, o Console exibirá o nome de todos os estados existentes no XML. Importante: não esqueça de adicionar uma diretiva using no topo do seu código para o namepsace System.Xml.Linq ou ele não vai compilar.

static void Main(string[] args)
        {
            XDocument xdoc = XDocument.Load(@"C:\Users\Fernando\Documents\visual studio 2010\Projects\Xml2Bd\Xml2Bd\XML\Estados.xml");
            foreach (var estado in xdoc.Descendants("ESTADOS").Elements())
            {
                Console.WriteLine(estado.Element("NOME").Value);
            }
        }

Se você fez o exemplo anterior de forma correta, já imagina como proceder para terminar o script sozinho, mas vamos terminar juntos. Incremente seu código conforme o exemplo abaixo, fazendo com que você instancie um Xml2BDEntities (nome gerado automaticamente para o contexto de dados do nosso banco Xml2Bd) e adicione um novo objeto Estado para cada registro Estado no XML.

static void Main(string[] args)
        {
            //carregando o XML
            XDocument xdoc = XDocument.Load(@"C:\Users\Fernando\Documents\visual studio 2010\Projects\Xml2Bd\Xml2Bd\XML\Estados.xml");
            //carregando o banco de dados
            Xml2BdEntities contextoDados = new Xml2BdEntities();

            foreach (var estado in xdoc.Descendants("ESTADOS").Elements())
            {
                //instanciando o novo estado a ser inserido
                Estado uf = new Estado();
                uf.ID = estado.Element("ID").Value;
                uf.Nome = estado.Element("NOME").Value;
                uf.Sigla = estado.Element("SIGLA").Value;
                uf.IDCapital = int.Parse(estado.Element("IDCAPITAL").Value);
                
                //adicionando ao contextodados
                contextoDados.AddToEstado(uf);    

                //imprimindo na tela para verificação
                Console.WriteLine(estado.Element("NOME").Value);
            }

            //persistindo as inserções diretamente no banco
            contextoDados.SaveChanges();
        }

Voilá! Aqui temos nosso código de migração XML para Sql Server 100% pronto! Se você for na sua tabela de Estados encontrará  todos os registros devidamente inseridos. O código para inserir as cidades no banco é praticamente o mesmo, mudando apenas a entidade a ser salva (Cidade) e os elementos do XML a serem percorridos. O referido código se encontra 100% pronto na solution que disponibilizei para download no início do post, se o que você queria era apenas co0locar os estados e cidades dos arquivos no seu BD, basta executar.

Considerações Importantes

Note que tive uma preocupação importante neste código que foi a de não instanciar múltiplos contextos de dados (Xml2BdEntities) para não sobrecarregar a memória do programa e nem encher o banco de requisições inúteis. Também efetuei somente uma operação de inserção no banco contendo todos os registros a serem adicionados, feito evidenciado pela chamada contextoDados.SaveChanges() na última linha de código, que nada mais faz do que persiste as entidades alteradas diretamente o banco de dados físico. Essas preocupações são o mínimo que se espera de um código ágil (afinal chamadas repetitivas ao banco de dados tornam a aplicação lenta) e escalável (nunca subestime a sua capacidade de escrever programas que derrubem a pilha de memória do servidor). E se eu quisesse migrar informações de uma base MySQL para uma SQL Server? Assunto para outro post...

Por hoje é só pessoal. Até o próximo post!

Tags: , , , ,

BD | Entity Framework | Linq | SQl Server

1

Base de dados com todos estados e cidades brasileiras

by Luiz Jr 19. dezembro 2010 15:07

Há alguns meses atrás estava desenvolvendo uma aplicação e por um motivo específico da mesma eu necessitava de uma base de dados com todos os estados brasileiros, todas cidades brasileiras, saber as capitais de cada estado e os DDDs de cada cidade. Dei várias "Googladas" e não encontrei nada. Ao que parece ninguém disponibilizou tal base de dados publicamente, embora todos esses dados sejam públicos e facilmente encontrados em sites como do IBGE. Como "coder" que sou, acabei desenvolvendo um script que lia uma página do IBGE e retirava as informações que me importavam, neste caso, a lista de cidades de cada estado. Mais uns minutos pesquisando e obtive as informações do DDD através do site da Embratel. Atando as pontas e mais um pouco de código e voilá! Uma base de dados prontinha contendo todas as informações geo-políticas das quais necessitava!

Como todo bom samaritano, tratei de guardar tal base para disponibilizá-la mais tarde quando tivesse um blog próprio. Opa! Segue abaixo o link para download de dois arquivos XML. O Estados.xml contém os dados referentes aos estados brasileiros, suas siglas e capitais. No Cidades.xml você tem uma imensa lista que acredito que abranja no mínimo 90% das cidades brasileiras (a culpa é do site do IBGE que é desatualizado) com o estado ao qual pertence e seu DDD (sim, não é apenas um DDD por estado). O quê, você não sabe como fazer para transformar esse XML na sua base de dados favorita? Então não perca meu próximo post...

Se você acabar utilizando tais arquivos em seu sistema, não esqueça de por meu nome nos agradecimentos, hehehehe.

Update em 02/01/2010: um pedido de desculpas aos leitores mineiros. Acabei de atualizar os arquivos com o estado de Minas Gerais (Estados.xml), as cidades de Minas Gerais (Cidades.xml) e para compensar o erro, adicionei um outro XML para download, o Regioes.xml com dezenas de regiões do Brasil, como Baixada Santista, Serra Gaúcha, entre outras, para facilitar a vida de quem esteja desenvolvendo (assim como eu) aplicações que usam dados geográficos como filtro para alguma busca. Agora o arquivo de cidades possui 5537 cidades brasileiras, bem pertinho dos 5565 aproximados que o IBGE diz que nosso Brasil possui.

Tags: ,

BD | XML

Powered by BlogEngine.NET 1.6.1.0
Design por Laptop Geek, adaptado por onesoft e personalizado por mim.