Compartilhar via


Este artigo foi traduzido por máquina.

O programador

Por dentro do SQLite

Ted Neward

Baixe o código de exemplo

Ted NewardAcompanhando o tema desse problema, é hora de voltar para as raízes do SQL e bancos de dados relacionais. Naturalmente, assim parece apropos escrever algo sobre o SQL Server, algo sobre suas novas melhorias de desempenho ou conjunto de recurso ou whatnot, mas não é meu estilo. Don fique comigo errada, é um grande banco de dados do SQL Server e altamente recomendada para esses cenários de classe empresarial “ ferro de passar grande ”, mas nem todo problema exige (como uma vez um amigo colocá-lo) um “ honkin grande ’ centralizado do banco de dados. ”

Na verdade, os desenvolvedores têm muito tempo usado bancos de dados relacionais simplesmente como um lugar “ colocar material para a próxima vez ” — coisas como configuração de opções, as configurações do usuário, os valores de internacionalização e assim por diante. Embora seja conveniente às vezes, para colocá-los em uma instância do SQL Server centralizada, em alguns casos, especialmente em cenários de cliente rico (e cenários de cliente rico especialmente o Microsoft Silverlight ou do Windows 7 do telefone), manter uma conexão constante a instância do SQL Server é inviável na melhor das hipóteses e freqüentemente flat-out simplesmente impossível.

Os desenvolvedores Don necessariamente desejam desistir o poder e flexibilidade de um banco de dados relacional, mas às vezes o mesmo SQL Server Express é muito pesada uma instalação. O que é a ser feito?

Vá luz, é claro: SQLite para ser preciso.

Apresentando o SQLite

Conforme descrito pelo seu site da Web (sqlite.org) “ SQLite é uma biblioteca de software que implementa um mecanismo de banco de dados SQL autônomo, sem servidor, configuração zero, transacional. ” Os elementos-chave dessa instrução giram em torno do substantivo “ biblioteca ”. Diferentemente do SQL Server usa um assembly de cliente para enviar solicitações a um servidor de análise e a execução, SQLite fica inteiramente dentro do processo de cliente, tornando-o como um banco de dados “ incorporado ”. O espaço de execução de um banco de dados SQLite durante o uso é um único arquivo armazenado posicionado sobre o sistema de arquivos do cliente e geralmente o rastro da instalação é igualmente pequeno.

Por tudo isso, o banco de dados SQLite é incrivelmente rico em recursos, que oferece suporte para a maior parte da especificação SQL-92, menos algumas coisas (descrita mais detalhadamente no site da SQLite Web) como, por exemplo, à direita e FULL OUTER JOIN, ALTER TABLE alguns disparam suporte, GRANT/REVOKE e a gravação em modos de exibição. O notável é a quantidade é suportada, incluindo as transações e uma ampla variedade de tipos de dados. Embora provavelmente seja além da credibilidade esperar que um esquema de banco de dados do SQL Server irá porta para SQLite sem modificações, é razoável pressupor que relativamente simples (ou seja, não tirar proveito de recursos ou tipos específicos do SQL Server) com problemas ao mínimo de esquema será porta. Isso torna SQLite ideal para cenários em que apenas um “ SQL leve ” é necessária.

Caso haja preocupações sobre sua aplicabilidade ou a estabilidade, SQLite lentamente está fazendo seu caminho em uma variedade de ambientes “ leves ” — ele já aparece dentro do navegador Mozilla Firefox (para suporte a HTML 5), bem como os ambientes Symbian, iOS e Android, entre outros. Em outras palavras, isso é como a “ outra metade ” do mundo do desenvolvimento (ou seja, não-­ centrados na Microsoft) tem o banco de dados leve. SQLite continua a apreciar o desenvolvimento contínuo e corrigindo, tornando-se uma aposta segura bem como um mecanismo SQL de um mínimo de bug.

É claro que, nenhum banco de dados estaria completo sem algum tipo de interface do administrador e SQLite não desaponta. Ele tem uma ferramenta de linha de comando do console para acessar e manipular os bancos de dados SQLite — mas que não pode impressionar o seu administrador do sistema tudo o que muito. Felizmente, a comunidade de código-fonte aberto possui várias ferramentas de SQLite disponíveis (uma longa lista deles está no site da SQLite Web), mas se você precisar apenas de uma ferramenta de como o analisador de consulta rápida, tente SQLite administrador, uma ferramenta gratuita disponível para download em sqliteadmin.orbmu2k.de de .

Goin ’ nativo

SQLite foi desenvolvida desde o início para ser um banco de dados para o desenvolvedor de código nativo, por isso, ele é implementado como uma DLL de C/C++ nativo. Esse tipo nativo de SQLite é um blessing e um praguejar: blessing, pois ela reduz sem muita sobrecarga (como a das atravessando a rede para o servidor e voltar novamente) do que o tempo total necessário para executar uma instrução SQL de determinado; mas curse que, como o banco de dados SQLite original é uma DLL de C/C++ nativo, obtendo a ele a partir de um aplicativo baseado no .NET Framework da Microsoft pode ser um desafio.

Felizmente, o desenvolvedor do .NET Framework miolos percebe que o acesso a uma DLL nativa é realmente apenas um exercício em declarações P/Invoke e é relativamente fácil criar uma classe de invólucro em torno as declarações nativas exposto na SQLite DLL. Na verdade, para as noções básicas, assim como acontece com tantas coisas na comunidade de código-fonte aberto, ele é já foi feito, navegue até switchonthecode.com/tutorials/csharp-tutorial-writing-a-dotnet-wrapper-for-sqlitede e encontramos um conjunto de trabalho de declarações P/Invoke, já colocados, mostrado em do Figura 1.

Figura 1 do declarações P/Invoke

namespace SQLiteWrapper
{
  public class SQLiteException : Exception
  {
    public SQLiteException(string message) :
      base(message)
      { }
  }

  public class SQLite
  {
    const int SQLITE_OK = 0;
    const int SQLITE_ROW = 100;
    const int SQLITE_DONE = 101;
    const int SQLITE_INTEGER = 1;
    const int SQLITE_FLOAT = 2;
    const int SQLITE_TEXT = 3;
    const int SQLITE_BLOB = 4;
    const int SQLITE_NULL = 5;

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_open")]
      static extern int sqlite3_open(string filename, out IntPtr db);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_close")]
      static extern int sqlite3_close(IntPtr db);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_prepare_v2")]
      static extern int sqlite3_prepare_v2(IntPtr db, string zSql,
        int nByte, out IntPtr ppStmpt, IntPtr pzTail);
    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_step")]
      static extern int sqlite3_step(IntPtr stmHandle);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_finalize")]
      static extern int sqlite3_finalize(IntPtr stmHandle);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_errmsg")]
      static extern string sqlite3_errmsg(IntPtr db);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_count")]
      static extern int sqlite3_column_count(IntPtr stmHandle);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_origin_name")]
      static extern string sqlite3_column_origin_name(
        IntPtr stmHandle, int iCol);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_type")]
      static extern int sqlite3_column_type(IntPtr stmHandle, int iCol);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_int")]
      static extern int sqlite3_column_int(IntPtr stmHandle, int iCol);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_text")]
      static extern string sqlite3_column_text(IntPtr stmHandle, int iCol);

    [DllImport("sqlite3.dll", EntryPoint = "sqlite3_column_double")]
      static extern double sqlite3_column_double(IntPtr stmHandle, int iCol);
  }
}

A alta fidelidade de P/Invoke para as APIs de C/C++ torna isso um processo relativamente simples — a API SQLite usa um ponteiro não processado para representar o banco de dados próprio, P/Invoke vê como uma System. IntPtr, e vez com tanta freqüência a API SQLite usa um ponteiro para um int como um parâmetro para que ele possa modificar o conteúdo, descrito por P/Invoke com o c# “ out ” palavra-chave. (Para obter mais informações sobre o P/Invoke, consulte pinvoke.codeplex.com de ).

Vou me referir você ao site SQLite para a maioria dos detalhes sobre como usar a API SQLite, mas em como abrir um banco de dados, executar uma consulta e, em seguida, feche o banco de dados rapidamente mostraria algo como do Figura 2.

De Abrir um banco de dados, executar uma consulta e fechando o banco de dados, a Figura 2

static void NativeMain()
 {
   // Open the database--db is our "handle" to it
   IntPtr db;
   if (SQLiteNative.sqlite3_open(@"cities.sqlite", out db) 
     == SQLiteNative.SQLITE_OK)
     {
       // Prepare a simple DDL "CREATE TABLE" statement
       string query = 
         "CREATE TABLE City " + 
         "(name TEXT, state TEXT, population INTEGER)";
       IntPtr stmHandle;
       if (SQLiteNative.sqlite3_prepare_v2(db, query, query.Length,
         out stmHandle, IntPtr.Zero) != SQLiteNative.SQLITE_OK)
       {
         // Something went wrong--find out what
         var err = SQLiteNative.sqlite3_errmsg(db);
       }
       if (SQLiteNative.sqlite3_step(stmHandle) != 
         SQLiteNative.SQLITE_DONE)
       {
         // Something went wrong--find out what
         var err = SQLiteNative.sqlite3_errmsg(db);
       }
       if (SQLiteNative.sqlite3_finalize(stmHandle) != 
         SQLiteNative.SQLITE_OK)
       {
         // Something went wrong--find out what
         var err = SQLiteNative.sqlite3_errmsg(db);
       }

     // ... Now that we've created a table, we can insert some
     // data, query it back and so on

     // Close the database back up
     SQLiteNative.sqlite3_close(db);
     }
  }

Eu sou Feelin ’ Mighty Low

A coisa mais surpreendentes sobre essa API é que ele é um pouco do lado de nível inferior. Se você for um golpe antigo de C++ como eu, isso pode ser uma coisa boa, fornecendo-em uma oportunidade maravilhosa para reminisce sobre o Good dias antiga, quando as homens eram homens, a memória era gerenciada à mão e mulheres swooned em partes cocktail sobre nossas assustadores histórias de perseguir pressionada untamed ponteiros em wilds do Windows 95 … mas para o restante desses c# whipper-snappers, esses Johnny-vem-Latelys que realmente desejam realizar produtivo trabalho, é um pouco muito Low para o início, por assim dizer. O que é necessário é um boa abstração invólucro essa API para torná-lo mais gerenciável e cortar para baixo o número de linhas de código necessárias para usá-lo.

Disposição esse em uma única classe nem tão difícil, especialmente porque o System. data fornece algumas classes de BOM podem lidar com a maior parte da interação da API de usuário. Mostrando todos os detalhes dessa classe de wrapper, chamada SQLite, é um pouco muito longa para incluir aqui, mas do Figura 3 mostradas as declarações de dar uma indicação muito clara de como deve ser usado.

A Figura 3 do Declarations da classe de wrapper SQLite

public class SQLite : IDisposable
  {
    private IntPtr _db; //pointer to SQLite database
    private bool _open; //whether or not the database is open

    /// <summary>
    /// Opens or creates SQLite database with the specified path
    /// </summary>
    /// <param name="path">Path to SQLite database</param>
    public void OpenDatabase(string path);

    /// <summary>
    /// Closes the SQLite database
    /// </summary>
    public void CloseDatabase();

    /// <summary>
    /// Executes a query that returns no results
    /// </summary>
    /// <param name="query">SQL query to execute</param>
    public void ExecuteNonQuery(string query);

    /// <summary>
    /// Executes a query and stores the results in
    /// a DataTable
    /// </summary>
    /// <param name="query">SQL query to execute</param>
    /// <returns>DataTable of results</returns>
    public DataTable ExecuteQuery(string query);
  }

Usá-lo, em seguida, ficaria algo semelhante ao exemplo do Figura 4.

Figura 4 de usar a classe de wrapper SQLite

static void NativeWrapperMain()
  {
    using (SQLite db = new SQLite("persons.sqlite"))
    {
      db.ExecuteNonQuery("CREATE Table Persons " +
        "(first TEXT, last TEXT, age INTEGER)");

      db.ExecuteNonQuery("INSERT INTO Persons (first, last, age) " +
        "VALUES ('Aaron', 'Erickson', 38)");
      db.ExecuteNonQuery("INSERT INTO Persons (first, last, age) " +
        "VALUES ('Rick', 'Minerich', 29)");
      db.ExecuteNonQuery("INSERT INTO Persons (first, last, age) " +
        "VALUES ('Talbott', 'Crowell', 35)");

      DataTable table = db.ExecuteQuery("SELECT * FROM Persons");

      foreach (DataRow row in table.Rows)
      {
        Console.WriteLine("{0} {1} {2}", row[0], row[1], row[2]);
      }
    }
  }

Claramente, há mais operações que poderiam ser adicionadas à classe de wrapper SQLite do Figura 4, mas ele já tem a funcionalidade necessária barebones, graças em parte à natureza maravilhosa independente de banco de dados do núcleo do DataTable/DataRow/DataColumn classes em System. data.

Abstrações de abstrações

De alguma forma, a vantagem do banco de dados SQLite é o design de nível inferior e a implementação —, significa que pode ser incorporada “ atrito ” envolvido em usá-lo é bastante claro.Adicione as classes de wrapper, certifique-se a DLL SQLite é um local acessível para o programa (geralmente colocando-o para o diretório com o executável) e agora você está escrevendo instruções SQL como um defensor.Presumindo que esse é o que você deseja fazer, é claro.

Mas é provável que a maior parte significativa dos desenvolvedores do .NET Framework estão fora de prática no gerenciamento inteiramente “ manualmente ” por meio de APIs do console de um banco de dados do SQL, nunca sabia como fazê-lo ou apenas deseja deixar nesse mundo.O ambiente moderno do .NET Framework fornece muitas das ferramentas para criar e gerenciar o esquema relacional sentir positivamente primitiva que voltando a essa abordagem manual e mais importante, improdutivo.

Além disso, o Microsoft já gastou esforço de criação de uma API que descreve com eficiência a maioria das coisas que um programador quer fazer em relação a um banco de dados relacional e muitas dessas ferramentas (o LINQ para SQL, Estrutura de Entidade e até mesmo o designer do Visual Studio) são criadas acima disso API.Eu me referir, é claro que, para o ado.net e seu modelo de provedor.Não ter a capacidade de slide SQLite “ embaixo ” ado.NET significa que tudo o que coolness não está disponível para o desenvolvedor usando SQLite e que parece como uma shortcoming bastante significativa.A solução, em seguida, é criar um provedor ado.net para SQLite.

Como nós já tenha descoberto, uma das coisas interessantes sobre a comunidade de código-fonte aberto é que há uma chance muito bom que tudo o que você deseja para fazer, alguém já o tenha feito, e isso não é diferente.System.Data.SQLite, disponível para download em sqlite.phxsoftware.com, é um provedor completo do ado.NET 3. 5, o que significa que tudo o que um desenvolvedor pode fazer com um provedor de banco de dados relacional de cliente/servidor tradicional está disponível para SQLite desenvolvedor, incluindo todo o designer do Visual Studio oferecem suporte, bem como o LINQ e Estrutura de Entidade.

Usar System.Data.SQLite é bastante direto.Pegue o download (qualquer origem, para poder criá-la e examinar o código para ver como tudo funciona — esse é um bom exemplo para trabalhar para saber como criar um provedor ado.net, se você estiver curioso — ou apenas os binários de captura, se você quiser ir para a “ done ” mais rapidamente).Solte os bits em algum lugar no seu disco rígido, System.Data.SQLite.dll de referência de projeto e de vida é bom.Não é surpresa que as classes de API moram System.Data.SQLite e assim que for referenciadas, você pode escrever um bom código ado.net de ol' no banco de dados, conforme mostrado no do Figura 5.

A Figura 5 usando System.Data.SQLite

static void ManagedWrapperMain()
{
  var connStr = new SQLiteConnectionStringBuilder() 
    { DataSource = "persons.sqlite" };
  using (SQLiteConnection conn = new SQLiteConnection(connStr.ToString()))
  {
    conn.Open();
    SQLiteCommand cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT COUNT(*) FROM Persons";
    var ct = cmd.ExecuteScalar();
    Console.WriteLine("Count = {0}", ct);

    cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT * FROM Persons";
    SQLiteDataReader reader = cmd.ExecuteReader();
    DataTable dt = new DataTable();
    dt.Load(reader);
    foreach (DataRow row in dt.Rows)
    {
      Console.WriteLine("{0} {1} {2}", row[0], row[1], row[2]);
    }
  }
}

Até aqui, tudo bem. Quando o código é executado a partir de um Visual Studio 2005 ou o projeto de 2008, tudo está funcionando perfeitamente. Mas quando o código é executado a partir do Visual Studio 2010, um erro é exibida, alegando “ exceção não tratada: System.IO.FileLoadException: Assembly de modo misto é construído em relação a versão v2.0.50727 do runtime e não pode ser carregado no runtime 4. 0 sem informações de configuração adicionais. ” Um assembly de modo misto, para aqueles que não tenha ouvido o termo antes, é um assembly que contém o Microsoft Intermediate Language gerenciado e nativo x 86 instruções de assembly. Isso, obviamente, não é boa, em dois níveis — um problema óbvio é, precisamos obter o código para trabalhar e dois, se este é um assembly de modo misto, irá criar alguns problemas ao usar SQLite em outros ambientes, como o asp.net.

O primeiro problema é facilmente resolvido com a adição de um arquivo app. config, que informa o 4. 0 do CLR para carregar o assembly de modo misto:

<?xml version="1.0"encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>
</configuration>

Um problema maior é que um número de ambientes Don oferecem suporte a módulos (assemblies) de modo misto, e em qualquer circunstância, há uma determinada aesthetic envolvido aqui. Para uma variedade de motivos, uma solução gerenciada por todos os seria preferível, mas porque a DLL SQLite é o código nativo, isso seria difícil. O que seria interessante é uma porta do código SQLite base para c#, mantidas perto para o C original quanto possível.

Tudo-gerenciados

Uma vez, a comunidade de código-fonte aberto fornece quando solicitado, e nesse caso, era um projeto chamado “ c#-SQLite, ” disponível em code.google.com/p/csharp-sqlite de . Ele aparentemente iniciado como “ um exercício para aprender a linguagem c# ” por portar o código e o wiki associado tiver alguma discussão sobre o autor fez para gerenciar a porta, mas o upshot é que agora temos exatamente o que precisávamos de: uma versão de SQLite gerenciado de todos os.

Usá-lo requer o download de fontes de projeto, abrir o projeto e iniciando fora de uma compilação. Um número de projetos de código-fonte aberto, c#, como - SQLite consiste em vários projetos, mas cada um deles está contido em seu próprio arquivo de solução, portanto, talvez você precise abrir mais de uma solução. (Ou simplesmente disparar as compilações de linha de comando com o MSBuild — tudo o que funciona melhor.)

Depois que ele é criado, adicione o c# - adicionar assembly SQLite (Community.C ­­ pequenos ­ SQLite) para o projeto e para o suporte do ado.net, o c# - cliente SQLite assembly (Community.CsharpSqlite.SQLiteClient.dll) também. Uma vez, todos os recursos do SQLite estão disponíveis para nós por meio de um provedor ado.net, que é possível reescrever quase exatamente o mesmo código mostrado anterior (consulte do Figura 6).

Figura 6 usando c#-SQLite

Static void AllManagedMain()
{
  SqliteConnection conn = 
    New SqliteConnection(@"Version=3,uri=file:persons.sqlite");
  conn.Open();
  try
  {
    SqliteCommand cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT COUNT(*) FROM Persons";
    var ct = cmd.ExecuteScalar();
    Console.WriteLine("Count = {0}", ct);

    cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT * FROM Persons";
    SqliteDataReader reader = cmd.ExecuteReader();
    while (reader.Read())
    {
      Console.WriteLine("{0} {1} {2}", reader[0], reader[1], reader[2]);
    }
  }
  finally
  {
    conn.Close();
  }
}

Observe como as APIs são quase idênticas para a versão anterior de modo misto (apenas os nomes de classe foram alterados e até mesmo, em seguida, é realmente apenas uma questão de caso: “ SQLite ” vs.“ Sqlite ” como prefixo, por exemplo), mas agora, podemos obter todas as qualidades SQLite sem os problemas de segurança potenciais (se houver) de uma DLL de modo nativo.

Limitações

Apesar da natureza maravilhosa SQLite, é importante entender suas limitações, se a decisão entre o uso de SQLite e o SQL Server deve ser feita com qualquer grau de sanidade.SQLite não irá oferecer todos os recursos do SQL Server — longe de.O banco de dados SQLite ainda não que que os desenvolvedores de usá-lo para usar vários segmentos, muito menos acessá-lo a partir de vários threads.Na verdade, é justo dizer que se dois programas que o acesso a um banco de dados SQLite simultaneamente, ele tem provavelmente tempo para atualizar para uma instância do SQL Server Express (caso contrário).

Principais áreas do SQLite “ vitória ” será praticamente das mesmas áreas em que arquivos do Access utilizados para ocupar, com uma sintaxe de SQL-92 quase concluída para fazer isso, junto com uma capacidade de ler arquivos de banco de dados usados por outros ambientes (Python, Perl e assim por diante).Também é uma área altamente interessante, especialmente para o armazenamento local usá-lo a partir de clientes do Silverlight ou do telefone – adequar-se a um banco de dados SQLite no Silverlight armazenamento isolado daria desenvolvedores portátil (em que ele possa viajar com o código do Silverlight) o banco de dados para armazenar dados locais, por exemplo.Usá-lo criteriosamente e SQLite Arredonda check-out de um continuum de banco de dados relacional das opções de funcionalidade para peso.

Novamente, se houver um tópico específico que você deseja ver exploradas, Don hesite em soltar-me uma anotação.Uma maneira bastante real, é a coluna, afinal de contas.

Codificação feliz!

Ted Neward é uma entidade de segurança com Neward & Associates, uma empresa independente especializado em sistemas de plataforma de .NET Framework e Java corporativo.Ele escreveu mais de 100 artigos, é um MVP de c#, palestrante da INETA e autor ou co-autor de diversos livros, incluindo o disponível em breve “ Professional F # 2. 0 ” (Wrox).Ele consulta e mentors regularmente.Entrar em ted@tedneward.com de e leia seu blog em blogs.tedneward.com de .