Páginas

Pesquisar neste blog

Mostrando postagens com marcador .NET. Mostrar todas as postagens
Mostrando postagens com marcador .NET. Mostrar todas as postagens

28 de fevereiro de 2019

Criando um Arquivo de Log em C#

Uma boa solução para descobrir bugs no software quando o software está em fase de teste ou até mesmo instalado é criando um arquivo texto de log, que pode ser armazenado no diretório da própria aplicação. Um cuidado que observei na criação de um arquivo de log é o método que utilizamos para criar o arquivo.
Inicialmente utilizei a classe File e o método Create do namespace System.IO:
         // Verifica se o arquivo de log da aplicação já existe
         if (!File.Exists("log_aplicacao.txt"))
         {
             File.Create("log_aplicacao.txt");
         }
                  
O código acima funciona sem problemas desde que o software execute várias outras operações antes de tentar escrever no arquivo de log. Se a próxima operação for por exemplo gravar a iniciação do software logo após criar o arquivo, o software apresentará uma exceção com uma mensagem parecida com “O processo não pode acessar o arquivo log.txt, porque ele está sendo usado por outro processo”.
Esta exceção ocorre pois enquanto o arquivo está sendo criado  o objeto FileStream criado por este método tem um valor default (FileShare de None), ou seja, nenhum outro processo ou código pode acessar o arquivo criado até que o handle do arquivo original seja fechado.

Uma possível solução foi utilizar a classe FileStream e passar o parâmetro FileShare como ReadWrite.
         // Cria o arquivo de log da aplicação
         FileStream fs = new FileStream("log_aplicacao.txt", FileMode.Create,
             FileAccess.ReadWrite, FileShare.ReadWrite);
         fs.Close();
Desde modo logo após a criação do arquivo já é possível escrever no arquivo de log. Segue um exemplo para gravar no log da aplicação a data e hora em que a aplicação foi iniciada.
        static void Main(string[] args)
        {
            try
            {
                 // Verifica se o arquivo de log da aplicação já existe
                if (!File.Exists("log_aplicacao.txt"))
                {
                    // Cria o arquivo de log da aplicação
                    FileStream fs = new FileStream("log_aplicacao.txt", FileMode.Create,
                        FileAccess.ReadWrite, FileShare.ReadWrite);
                    fs.Close();
                }
                // Cria o objeto StreamWriter, passando como parâmetro o nome do
                // arquivo criado anteriormente e o valor booleano true
                // indicando que as informações devem ser acrescentadas no arquivo
                // toda vez que a aplicação for iniciada criando desta forma um
                // arquivo de log com um histório de iniciação da aplicação.
                using (StreamWriter sw = new StreamWriter("log_aplicacao.txt", true))
                {
                    sw.WriteLine("A aplicação iniciada em " + DateTime.Now.ToString());
                    sw.Flush();
                    sw.Close();
                }
                Console.WriteLine("O arquivo foi gerado com suceso!");
            }
            catch (IOException ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.ReadKey();
        }

Tipos de LINQ

O LINQ é um modelo de programação que introduz a consulta e manipulação de dados dentro do conceito da linguagem .NET. Entretanto o suporte completo ao LINQ requer algumas extensões na linguagem utilizada.

O código abaixo exibe um exemplo de utilização do LINQ para fazer uma consulta na tabela Customers da base de dados Northwind.

        static void Main(string[] args)
        {
            // DataContext utiliza a connectionstring
            NorthwindDataContext db = new NorthwindDataContext();

            // Seleciona a tabela para executar a consulta
            Table<Customer> Customers = db.GetTable<Customer>();

            //Consulta clientes da cidade London
            var query =
                from cust in Customers
                where cust.City == "London"
                select cust;

            Console.WriteLine("Empresa".PadRight(25) + "Nome".PadRight(25) + "Cidade");
            foreach (var customer in query)
                Console.WriteLine("{0}{1}{2}", customer.CompanyName.PadRight(25),
                    customer.ContactName.PadRight(25), customer.City);
            Console.ReadKey();

        }
O resultado do código é:

Capture1
O código traz os dados de todos os Customers cuja cidade seja “London”. A consulta foi feita em uma Base de Dados SQL, mas a mesma consulta poderia ser utilizada se Customer fosse uma tabela em um DataSet ou uma classe que descreve uma tabela.

Um outro aspecto importante da integração da linguagem é a checagem de tipo. A sintaxe das expressões de consulta não tem o compromisso da checagem de tipo, pois os dados são sempre fortemente tipados, incluindo as consultas de coleções ou entidades que são lidas ou retornadas.
Atualmente existem os seguintes tipos de LINQ:
  • LINQ to Objects
  • LINQ to ADO.NET que inclui LINQ to SQL, LINQ to Dataset e LINQ to Entities
  • LINQ to XML
Cada uma destas implementações é definida através de um conjunto de extensões de métodos que implementam os operadores necessários para que o LINQ rode com uma fonte de dados particular. O acesso destas características é controlado por Namespaces.

LINQ to Objects
O LINQ to Object tem o objetivo de manipular coleções de objetos, que podem estar relacionados entre si de forma hierarquica ou gráfica. O LINQ to Object não está limitado a coleções de dados gerados pelo usuário. Como exemplo segue uma consulta de todos os arquivos que estão localizados no diretório temporário maiores de 500KB ordenados de forma decrescente:
        static void Main(string[] args)
        {
            string tempPath = Path.GetTempPath();
            DirectoryInfo dirInfo = new DirectoryInfo(tempPath);

            var query =
                from f in dirInfo.GetFiles()
                where f.Length > 500000 // Arquivos maior que 500K
                orderby f.Length descending
                select f;

            foreach (var arq in query)
                Console.WriteLine("Name = {0}", arq.Name);

            Console.ReadKey();
        }

O resultado do código acima é similar a figura abaixo. O resultado vai depender do conteúdo existente no diretório temporário da máquina que está sendo utilizada.
image
LINQ to ADO.NET
O LINQ to ADO.NET inclui diferentes implementações que compartilham da necessidade de manipular dados relacionais.
  • LINQ to SQL: é um componente do projeto LINQ que tem a capacidade de executar consultas em uma Base de Dados relacional.
  • LINQ to Entities: É muito similar ao LINQ TO SQL, entretanto ao invés de usar uma Base de Dados física como uma camada de persistência, ele usa uma entidade conceitual o EDM (Entity Data Model). O resultado da abstração é uma camada independente da camada de dados física.
  • LINQ to DataSet: Permite fazer consulta em um DataSet.
O código abaixo exemplifica uma consulta feita em um dataset tipado utilizando o LINQ to Dataset:
        static void Main(string[] args)
        {
            // Inicializa o TableAdapter da base de dados Northwind
            dsNorthwindTableAdapters.OrdersTableAdapter taNorthwind =
                new dsNorthwindTableAdapters.OrdersTableAdapter();

            // Declaração do DataSet Tipado da base de dados Northwind
            dsNorthwind ds = new dsNorthwind();
            taNorthwind.Fill(ds.Orders);

            // Consulta que traz o Id e a data do pedido para pedidos
            // com data superior a 01/01/1998
            var query = from o in ds.Orders
                        where o.OrderDate >= new DateTime(1998, 01, 01)
                        select new { o.OrderID, o.OrderDate };

            Console.WriteLine("Id do Pedido\t" + "Data do Pedido");

            foreach (var order in query)
            {
                Console.WriteLine("{0}\t\t{1}", order.OrderID,
                    order.OrderDate);
            }

            Console.ReadKey();           
        }

LINQ to XML
O LINQ to XML oferece uma sintaxe um pouco diferente para operar com dados em XML, permitindo consulta e manipulação de dados.

O código abaixo exemplifica uma busca no arquivo XML que possui informações sobre uma coleção de livros.

Arquivo Xml utilizado como fonte da busca:
<?xml version="1.0" encoding="utf-8" ?>
<livros>
  <livro linguagem="csharp">
    <titulo>Introducao ao CSharp</titulo>
    <autor>Antonio Dias</autor>
    <ano>2002</ano>
  </livro>
  <livro linguagem="vb">
    <titulo>Introducao ao Visual Basic</titulo>
    <autor>Marcelo Gomes</autor>
    <ano>2005</ano>
  </livro>
  <livro linguagem="csharp">
    <titulo>CSharp Avancado</titulo>
    <autor>Jose Maria</autor>
    <ano>2008</ano>
  </livro>
</livros>
Código utilizado para trazer os livros cuja linguagem seja vb:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Carrega o arquivo Xml
            XElement root = XElement.Load("livros.xml");
            // Faz uma busca nos livros cujo atributo linguagem seja vb
            IEnumerable<XElement> linguagem =
                from el in root.Elements("livro")
                where (string)el.Attribute("linguagem") == "vb"
                select el;
            // Exibe o resultado da pesquisa
            foreach (XElement el in linguagem)
                Console.WriteLine(el);

            Console.ReadKey();           
        }
    }
}
Resultado obtido:
image
Como conclusão podemos observar que o LINQ é uma linguagem que permite uma uniformidade para acessar dados em diversas fontes de informações.

Insert, Update e Delete com LINQ

Em um dos projetos que estive trabalhando, precisei interagir com uma Base de Dados que possuia tabelas sem chave primária, e por motivos de compatibilidade com projetos anteriores não podia alterar as tabelas para adequar as boas práticas de desenvolvimento e inserir as chaves primárias.
No meu projeto eu precisava inserir, deletar e fazer atualizações nos registros das tabelas. Resolvi utilizar o LINQ para fazer estas operações.

O LINQ não permite a inserção, exclusão e atualização em tabelas que não possuam chave primária, por isso tive que utilizar um recurso para poder trabalhar com estas tabelas.

Criei uma base de dados com uma tabela exemplo sem chave primária, para poder demonstrar uma forma de resolver este tipo de problema. A base de dados chama-se Escola e a tabela Aluno.
Criei a classe DBML no Visual Studio que tem o seguinte formato:

AlunoDbml
Para inserir o registro de um aluno na tabela podemos utilizar o seguinte código:
        /// <summary>
        /// Executa uma instrucao Insert inserindo um aluno na
        /// base de dados.
        /// </summary>
        static void Insert()
        {
            try
            {
                EscolaClassesDataContext context = 
                    new EscolaClassesDataContext();
 
                Aluno aluno = new Aluno();
                aluno.Matricula = "67489";
                aluno.Nome = "Douglas Xavier";
                aluno.Idade = 35;
                aluno.Curso = "SQL Server";
                aluno.Endereco = "Rua Germano Souza, 675";
 
                context.Alunos.InsertOnSubmit(aluno);
                context.SubmitChanges();
 
                Console.WriteLine("Aluno inserido com sucesso!");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
Executando o método Insert() o aluno não é inserido na tabela e obtemos a seguinte mensagem de erro:
Can’t perform Create, Update or Delete Operations on Table(Aluno) because it has no primary key.
A mensagem indica que o LINQ não permite a inserção, exclusão ou atualização de registros em tabelas que não possuam chave primária.

Se testarmos o método Delete(), cujo código é exibido abaixo, obtemos a mesma mensagem de erro do método Insert():

        /// <summary>
        /// Executa uma instrucao Delete, apagando os dados de um aluno.
        /// </summary>
        static void Delete()
        {
            try
            {
                EscolaClassesDataContext context = new 
                    EscolaClassesDataContext();
 
                Aluno aluno = (from a in context.Alunos
                               where a.Idade == 22
                               select a).First();
 
                context.Alunos.DeleteOnSubmit(aluno);
                context.SubmitChanges();
                Console.WriteLine("O aluno foi apagado com sucesso!");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
Se testarmos o método Update(), cujo código é exibido abaixo, a atualização não será feita pois o método de Update do LINQ não gera exceção, por isso é muito dificil detectar qual problema está acontecendo, já que a atualização não é feita e nenhuma exceção é indicada.

        /// <summary>
        /// Executa uma instrucao Update atualizando um endereco de 
        /// um determinado aluno
        /// </summary>
        static void Update()
        {
            try
            {
                EscolaClassesDataContext context = new 
                    EscolaClassesDataContext();
 
                Aluno aluno = (from a in context.Alunos
                               where a.Nome == "Renato Borges"
                               select a).First();
                aluno.Endereco = "Av Otaviano Silva, 47";
 
                context.SubmitChanges();
                Console.WriteLine("A atualização foi realizada com" +
                    " sucesso!");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
Como resolver este problema se não podemos mexer na estrutura da tabela do banco de dados? A solução é criar uma chave primária na classe DBML. Esta chave pode ser definida em qualquer coluna. Esta definição não afeta a estrutura da tabela na base de dados, é somente um recurso para podermos trabalhar com LINQ em tabelas sem chave primária.

Para inserir uma chave primária em uma coluna da tabela Aluno, devemos alterar a propriedade PrimaryKey de False para True da coluna que servirá como chave primária.

Selecionei a coluna Matricula para servir de chave primária. Veja a figura abaixo de como ficou a definição da classe DBML.

ChavePrimaria 
Após a configuração da chave primária os métodos Insert(), Update() e Delete() podem ser executados.

Inseri o código para fazer um Select na tabela e visualizar todos os métodos sendo executados sem erros.
/// <summary>
/// Executa uma instrucao Select na base de dados.
/// </summary>
static void Select()
{
    // Cria a conexão com a base de dados Escola
    EscolaClassesDataContext context = new EscolaClassesDataContext();
 
    // Faz um SELECT na base de dados para imprimr a relação de alunos
    var queryAlunos = from a in context.Alunos
                      select a;
 
    if (queryAlunos.Count() > 0)
    {
        foreach (var item in queryAlunos)
        {
            Console.WriteLine("=========================================");
            Console.WriteLine("Matrícula: " + item.Matricula);
            Console.WriteLine("Nome: " + item.Nome);
            Console.WriteLine("Idade: " + item.Idade);
            Console.WriteLine("Curso: " + item.Curso);
            Console.WriteLine("Endereço: " + item.Endereco);
            Console.WriteLine("=======================================\r\n");
 
        }
    }
}
O código que utilizei no projeto para chamar todos os métodos foi:
        static void Main(string[] args)
        {
            Console.WriteLine("Executa o método Insert");
            Insert();
 
            Console.WriteLine("Executa o método Update");
            Update();
 
            Console.WriteLine("Executa o método Delete");
            Delete();
 
            Console.WriteLine("Executa o método Select");
            Select();
 
            Console.ReadKey();
        }
Segue a tela com o resultado dos métodos executados com sucesso:
Resultado
Os conceitos utilizados nos métodos Insert(), Update() e Delete() mostrados neste post podem ser utilizados normalmente, apenas acrescentei uma possível solução para o problema de precisar trabalhar com tabelas sem chaves primárias.

Obtendo as Propriedades de uma Figura em C#

Existem situações no projeto em que precisamos saber as propriedades de uma figura, como tamanho em Bytes, largura e altura em pixels ou a resolução, para determinarmos se a figura é adequada para a aplicação, ou se possui as propriedades corretas como tamanho, para fazer um download em um website.
Para obter as propriedades da figura podemos utilizar a classe abstrata Image. No código da Listagem 1 obtemos o tamanho em Bytes, a largura x altura em pixels e a resolução horizontal x resolução vertical em dpi.
Listagem 1
    class Program
    {
        static void Main(string[] args)
        {
            // Lê o arquivo da figura
            FileStream stream = new FileStream("imagem1.jpg", FileMode.Open);
            int imageSize = 0;
            int imageWidth = 0;
            int imageHeight = 0;
            float imageHorizontalDpi = 0.0f;
            float imageVerticalDpi = 0.0f;
 
            // Obtêm as propriedades da figura
            using (Image image = Image.FromStream(stream, false, false))
            {
                imageSize = Convert.ToInt32(stream.Length);
                imageWidth = image.Width;
                imageHeight = image.Height;
                imageHorizontalDpi = image.HorizontalResolution;
                imageVerticalDpi = image.VerticalResolution;                
            }
 
            Console.WriteLine("Tamanho da Figura em Bytes: " + 
                imageSize);
            Console.WriteLine("Largura x Altura da Figura: " + 
                imageWidth.ToString() + " x " + imageHeight + " pixels");
            Console.WriteLine("Resolução da Figura: " + imageHorizontalDpi + " x " +
                imageVerticalDpi + " dpi");
 
            Console.ReadLine();
        }
    }

Para a figura utilizada no código da Listagem 1 obtemos o seguinte resultado:
Tamanho da Figura em Bytes: 83794
Largura x Altura da Figura: 800 x 600 pixels
Resolução da Figura: 96 x 96 dpi

Como Ler Email Programaticamente em C#

Para fazer a leitura programaticamente de emails, existe uma biblioteca chamada OpenPop.NET, que é uma implementação open source de um cliente POP3 e um analisador MIME escrito em C#. Com esta biblioteca é possível ler as informações de email, tais como, origem, destino, assunto, mensagem, etc.

Para demonstrar a utilização desta biblioteca foi criado o projeto LerEmail no Visual Studio, que faz a leitura das informações como origem, destino assunto e mensagem dos 10 emails mais recentes recebidos na caixa de entrada de um servidor web.

Mas antes de utilizar a biblioteca é necessário incluir no projeto do Visual Studio. A biblioteca OpenPop.NET pode ser incluída utilizando-se a ferramenta de gerenciamento de pacotes NuGet do Visual Studio. Esta ferramenta pode ser acessada clicando com o botão direito sobre  a pasta References do projeto e selecionando a opção Manage NuGet Packages.
vs1
Na tela Manage NuGet Packages fazer uma busca pela biblioteca OpenPop.NET e clicar no botão Install, quando a biblioteca for localizada.
vs2
Após a inclusão da biblioteca no projeto do Visual Studio, pode-se  utilizar a classe Pop3Client() que possui os métodos GetMessageHeaders(int) e GetMessage(int) para obter as informações do cabeçalho da mensagem e do corpo da mensagem, respectivamente. Para obter estas informações é necessário passar para estes métodos o parâmetro número do email que deve ser lido.

A classe Pop3Client() faz a leitura dos emails utilizando um contador que  ordena em ordem decrescente o email, sendo o de maior número o email mais recente e o de menor número o email mais antigo. Para obter o número total de emails utilizar o método GetMessageCount().

Portanto para o caso de ler os emails mais recentes é necessário obter o número total de emails através do método GetMessageCount() e a partir dele ir decrementando o número até obter os emails desejados. Para o caso de ler os emails mais antigos iniciando-se, por exemplo, do primeiro email utilizar o número 1 até o número de emails desejados.

Segue o código do método BuscaEmail(string, int, string, string) que exemplifica como ler os 10 emails mais recentes da caixa de entrada.
        static void BuscaEmail(string nome_servidor_pop, int porta, string email, string senha)
        {
            // A instrução using faz a desconexão do servidor de email
            // e libera corretamente o objeto
            using (Pop3Client cliente_pop = new Pop3Client())
            {
                // Faz a conexão com o servidor
                cliente_pop.Connect(nome_servidor_pop, porta, false);

                // Faz a autenticação no servidor
                cliente_pop.Authenticate(email, senha, 
                    AuthenticationMethod.UsernameAndPassword);

                // Obtêm o número total de emails da caixa de entrada
                int numero_emails = cliente_pop.GetMessageCount();
                
                // Faz a leitura dos 10 emails mais recentes da caixa de entrada,
                // iniciando a partir do último email recebido.
                for (int i = 0; i < 10; i++)
                {
                    // Cabeçalho da mensagem
                    MessageHeader headers = cliente_pop.GetMessageHeaders(numero_emails);

                    // Email De:
                    RfcMailAddress emailDe = headers.From;

                    // Email Para:
                    // Faz a leitura de todos os emails Para, mas exibe somente o primeiro.
                    List<RfcMailAddress> emailParaList = headers.To;
                    string emailPara = string.Empty;
                    if (emailParaList != null && emailParaList.Count() > 0)
                    {
                        emailPara = emailParaList.First().Address;
                    }

                    // Assunto
                    string assunto = headers.Subject;

                    // Data de envio
                    DateTime data_envio = headers.DateSent;

                    // Verifica se o endereço de email De é válido
                    if (emailDe.HasValidMailAddress)
                    {
                        // Corpo do email
                        Message mensagem = cliente_pop.GetMessage(numero_emails);
                        string corpo_email = mensagem.MessagePart.GetBodyAsText();

                        // Imprime as informações do email
                        Console.WriteLine("De: " + emailDe);
                        Console.WriteLine("Para: " + emailPara);
                        Console.WriteLine("Assunto: " + assunto);
                        Console.WriteLine("Data de Envio: " + data_envio);
                        Console.WriteLine(corpo_email);
                        Console.WriteLine("".PadLeft(30, '-'));

                        // Decrementa o número de emails
                        numero_emails--;
                    }
                }
            }
        }

O método BuscaEmail(string, int, string, string) faz a conexão e autenticação com o servidor, obtêm o número total de emails e a partir daí faz a leitura dos 10 emails mais recentes da caixa de entrada e exibe em uma tela console as informações dos emails.

14 de dezembro de 2011

Como Usar SQLite em uma Aplicação .NET

A Base de Dados SQLite é conhecida por ser muito leve, rápida e por ser utilizada em várias aplicações que necessitam de uma base de dados confiável e robusta. Atualmente a base de dados SQLite é muito utilizada em dispositivos móveis como Android, Iphone e Ipad.

Antigamente para utilizar a base de dados SQLite em .NET era um pouco complicado, porque precisavamos utilizar as DLLs do SQLite através do PInvoke em .NET. Agora no site oficial do SQLite foi inserida a biblioteca  System.Data.SQLite para ser utilizada em aplicações .NET.

Para este post criei uma Aplicação Console para manipular as informações de uma base de dados de livros. Nesta aplicação o usuário poderá cadastrar, excluir, alterar e visualizar os livros existentes na base de dados.

Para utilizar a biblioteca do SQLite em .NET é necessário fazer o download da biblioteca e instalar na máquina de desenvolvimento. A biblioteca pode ser baixada no site oficial do SQLite.

No projeto Console no Visual Studio é necessário incluir uma referência à Dll System.Data.SQLite.dll, que normalmente é instalada no diretório C:\Arquivos de Programas\System.Data.SQLite\2008\bin.

No projeto Console foi criada a classe SqliteDb que possui os métodos para inserir, alterar e excluir dados da base SQLite,  a classe auxiliar Livro (Listagem 1) que é utilizada para armazenar os dados do livro que está sendo utilizado pelos outros métodos e a classe Program que possui o método Main e outros métodos auxiliares para executar a aplicação.

Listagem 1:
    public class Livro
    {
        public long id { get; set; }
        public string titulo { get; set; }
        public string autor { get; set; }
        public string ano { get; set; }
        public string genero { get; set; }
    }

A seguir são detalhados os métodos que fazem parte da classe SqliteDb:
Foram criadas duas constantes, uma para definir o nome da base de dados e a outra para definir a conexão com a base de dados SQLite.

        const string DATABASE_NAME = "livros_classicos.db";
        const string CONNECTION = "Data Source=" + DATABASE_NAME;




O método CreateDatabase() cria a base de dados livros_classicos.db, se não existir e cria a tabela livros:
        public void CreateDatabase()
        {
            SQLiteConnection conn = new SQLiteConnection(CONNECTION);
            try
            {
                if (!File.Exists(DATABASE_NAME))
                {
                    SQLiteConnection.CreateFile(DATABASE_NAME);

                    // Cria a tabela livros
                    conn.Open();
                    SQLiteCommand cmd = new SQLiteCommand();
                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = "CREATE TABLE livros (" +
                        "id INTEGER NOT NULL PRIMARY KEY," + 
                        "titulo TEXT," + 
                        "autor TEXT," + 
                        "ano_publicacao TEXT," + 
                        "genero TEXT)";

                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                }
            }
        }
O método InsertLivro(Livro) insere um livro na base:
        public bool InsertLivro(Livro livro)
        {
            bool resultado = false;
            SQLiteConnection conn = new SQLiteConnection();

            try
            {
                conn.ConnectionString = CONNECTION;
                conn.Open();

                SQLiteCommand cmd = new SQLiteCommand();
                cmd.Connection = conn;
                cmd.CommandType = CommandType.Text;

                cmd.Parameters.AddWithValue("@Titulo", livro.titulo);
                cmd.Parameters.AddWithValue("@Autor", livro.autor);
                cmd.Parameters.AddWithValue("@Ano", livro.ano);
                cmd.Parameters.AddWithValue("@Genero", livro.genero);

                cmd.CommandText = "INSERT INTO livros " +
                    "(titulo, autor, ano_publicacao, genero) " +
                    "VALUES (@Titulo, @Autor, @Ano, @Genero)";

                int registro = cmd.ExecuteNonQuery();

                // Verifica se o registro foi inserido
                if (registro == 1)
                {
                    resultado = true;
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                }
            }

O método SearchLivro(string) faz a procura de livros por título e retorna a lista de livros filtrados. Neste método optei em fazer uma busca utilizando o operador LIKE do SQL para fazer uma busca por padrão e não uma busca exata.

        public List<Livro> SearchLivro(string titulo)
        {
            List<Livro> livros = new List<Livro>();

            SQLiteConnection conn = new SQLiteConnection();

            try
            {
                conn.ConnectionString = CONNECTION;
                conn.Open();

                SQLiteCommand cmd = new SQLiteCommand();
                cmd.Connection = conn;
                cmd.CommandType = CommandType.Text;

                cmd.Parameters.AddWithValue("@Titulo", titulo);

                cmd.CommandText = "SELECT * FROM livros WHERE titulo LIKE '%"
                    + titulo + "%'";

                SQLiteDataReader reader = cmd.ExecuteReader();

                while (reader.Read() == true)
                {
                    Livro livro = new Livro();
                    livro.id = (long)reader["id"];
                    livro.titulo = (string)reader["titulo"];
                    livro.autor = (string)reader["autor"];
                    livro.ano = (string)reader["ano_publicacao"];
                    livro.genero = (string)reader["genero"];

                    livros.Add(livro);
                
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                }
            }

            return livros;
        }




O método UpdateLivro(Livro) atualiza os dados do livro.

        public string UpdateLivro(Livro livro)
        {
            string resultado = "";
            SQLiteConnection conn = new SQLiteConnection();

            try
            {

                conn.ConnectionString = CONNECTION;
                conn.Open();

                SQLiteCommand cmd = new SQLiteCommand();
                cmd.Connection = conn;
                cmd.CommandType = CommandType.Text;

                cmd.Parameters.AddWithValue("@Id", livro.id);
                cmd.Parameters.AddWithValue("@Titulo", livro.titulo);
                cmd.Parameters.AddWithValue("@Autor", livro.autor);
                cmd.Parameters.AddWithValue("@Ano", livro.ano);
                cmd.Parameters.AddWithValue("@Genero", livro.genero);

                cmd.CommandText = "UPDATE livros SET titulo=@Titulo, autor=@Autor," +
                    " ano_publicacao=@Ano, genero=@Genero WHERE id=@Id";

                int registroAtualizado = cmd.ExecuteNonQuery();


                if (registroAtualizado == 1)
                {
                    resultado = "O livro foi atualizado com sucesso.";
                }
                else
                {
                    resultado = "O livro não foi atualizado.";
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (conn != null)
                {
                    conn.Close();
                }
            }

            return resultado;
        }



O método DeleteLivro(string) apaga o livro filtrado, na base de dados, através do id do livro.

        public string DeleteLivro(string id)
        {
            string resultado = "";
            SQLiteConnection conn = new SQLiteConnection();

            try
            {
                conn.ConnectionString = CONNECTION;
                conn.Open();

                SQLiteCommand cmd = new SQLiteCommand();
                cmd.Connection = conn;
                cmd.CommandType = CommandType.Text;

                cmd.Parameters.AddWithValue("@Id", id);

                cmd.CommandText = "DELETE FROM livros WHERE id=@Id";

                int registroAtualizado = cmd.ExecuteNonQuery();


                if (registroAtualizado == 1)
                {
                    resultado = "O livro foi excluído com sucesso.";
                }
                else
                {
                    resultado = "O livro não foi excluído.";
                }
            }
            catch (Exception ex)
            {
                resultado = ex.Message;
            }

            return resultado;
        }
    }



A seguir são detalhados os métodos que fazem parte da classe Program:

O método Main é o responsável pela inicialização e execução da aplicação:

        static void Main(string[] args)
        {
            ConsoleKeyInfo key = new ConsoleKeyInfo();
            Console.WriteLine("Digite S a qualquer momento" +
                " para sair da aplicação!");

            SqliteDb sqlitedb = new SqliteDb();

            // Cria a base de dados se não existir
            sqlitedb.CreateDatabase();

            while (true)
            {
                Console.WriteLine("Digite I para incluir, A para alterar," +
                    " E para excluir, P para pesquisar e V para visualizar todos os livros:");

                // Lê a tecla digitada
                key = Console.ReadKey(true);

                // Sai da aplicação
                if (key.KeyChar == 'S' || key.KeyChar == 's')
                {                    
                    break;
                }

                // Pesquisa um livro por título
                if (key.KeyChar == 'P' || key.KeyChar == 'p')
                {
                    Console.WriteLine("Digite o título do livro a ser pesquisado:");
                    string titulo = Console.ReadLine();

                    // Exibe as informações do livro
                    BuscarLivro(titulo);
                }

                // Inclui um livro
                if (key.KeyChar == 'I' || key.KeyChar == 'i')
                {
                    // Lê as informações do livro a ser inserido
                    Livro livro = GetLivro();

                    // Insere o livro
                    if (sqlitedb.InsertLivro(livro) == true)
                    {
                        Console.WriteLine("O livro foi incluído com sucesso!");
                    }
                }

                // Exclui um livro
                if (key.KeyChar == 'E' || key.KeyChar == 'e')
                {
                    Console.WriteLine("Digite o título do livro a ser excluído:");
                    string titulo = Console.ReadLine();

                    // Buscar o livro
                    List<Livro> livros = BuscarLivro(titulo);

                    // Obtêm o Id do livro a ser excluído
                    string id = GetId(livros);

                    if (id != "")
                    {
                        // Exclui o livro
                        string resultado = sqlitedb.DeleteLivro(id);
                        Console.WriteLine(resultado);
                    }
                }

                // Altera um livro
                if (key.KeyChar == 'A' || key.KeyChar == 'a')
                {
                    Console.WriteLine("Digite o título do livro a ser alterado:");
                    string titulo = Console.ReadLine();

                    // Buscar livro
                    List<Livro> livros = BuscarLivro(titulo);

                    // Obtêm o Id do livro a ser alterado
                    string id = GetId(livros);

                    if (id != "")
                    {
                        // Lê as novas informações do livro a ser alterado
                        Livro livro = GetLivro();

                        // Atualiza o valor do id
                        livro.id = long.Parse(id); ;

                        // Altera as informações do livro
                        string resultado = sqlitedb.UpdateLivro(livro);
                        Console.WriteLine(resultado);
                    }
                }
            }
        }
O método possui um loop while que apresenta um menu para que o usuário possa selecionar a função que deseja executar ou sair da aplicação. Quando a aplicação é executada é apresentado o seguinte menu:

Digite S a qualquer momento para sair da aplicação!
Digite I para incluir, A para alterar, E para excluir, P para pesquisar e V para
visualizar todos os livros:


Além do método Main a classe Program possui também os métodos BuscarLivro(string), GetId(List<Livro) e GetLivro().

O método BuscarLivro(string) faz a busca dos livros por título e retorna uma lista de livros.

        private static List<Livro> BuscarLivro(string titulo)
        {
            SqliteDb sqlitedb = new SqliteDb();

            List<Livro> livros = sqlitedb.SearchLivro(titulo);
            Console.WriteLine();
            foreach (Livro item in livros)
            {
                Console.WriteLine(item.id + "\t" + item.titulo + "\t" + item.autor + "\t" +
                    item.ano + "\t" + item.genero);
            }
            Console.WriteLine();

            return livros;
        }

O método GetId(List<Livro> livros) faz a busca dos livros e retorna o id do livro.

        private static string GetId(List<Livro> livros)
        {
            // Digitar o id do livro a ser alterado se houver mais de 
            // um livro como resultado
            string id = "";
            if (livros.Count == 0)
            {
                Console.WriteLine("O livro não foi encontrado.");
            }
            else if (livros.Count == 1)
            {
                id = livros[0].id.ToString();
            }
            else
            {
                Console.WriteLine("Digite o id do livro a ser alterado:");
                id = Console.ReadLine();
            }

            return id;
        }



O método GetLivro() retorna um objeto livro com os detalhes do livro.

        private static Livro GetLivro()
        {
            Livro livro = new Livro();
            Console.WriteLine("Digite o título do livro:");
            livro.titulo = Console.ReadLine();
            Console.WriteLine("Digite o autor do livro:");
            livro.autor = Console.ReadLine();
            Console.WriteLine("Digite o ano de publicação do livro:");
            livro.ano = Console.ReadLine();
            Console.WriteLine("Digite o gênero do livro:");
            livro.genero = Console.ReadLine();

            return livro;

        }