Este artículo proviene de un motor de traducción automática.

El programador políglota

Dentro de SQLite

Ted Neward

Descargar el código de ejemplo

Ted NewardDe acuerdo con el tema de este problema, es el momento de volver a las raíces de SQL y bases de datos relacionales. Naturalmente, que, por tanto, resulta agregación para escribir algo acerca de SQL Server, algo sobre sus nuevas mejoras de rendimiento o el conjunto de características o whatnot, pero no es mi estilo. No me malinterpreten, SQL Server es una base de datos gran y muy recomendable para los escenarios de empresa “ hierro grande ”, pero no todos los problemas exige (como un amigo una vez ponerlo) un “ honkin grande ’ centralizada de la base de datos. ”

De hecho, los desarrolladores largo utilizaba las bases de datos relacionales simplemente como un lugar “ incluir material para la próxima vez ”: material como, por ejemplo, configuración de opciones, configuración de usuario, los valores de la internacionalización y así sucesivamente. Aunque es a veces es conveniente colocar en una instancia de SQL Server centralizada, en algunos casos, especialmente en escenarios de cliente enriquecida y escenarios de cliente enriquecido especialmente Microsoft Silverlight o de teléfono de Windows 7, mantener una conexión constante a la instancia de SQL Server es inviable en el mejor y con frecuencia sólo flat-out imposible.

Los desarrolladores no desean renunciar a la eficacia y flexibilidad de una base de datos relacional, pero a veces, incluso SQL Server Express es demasiado pesada de una instalación. ¿Qué es hacer?

Ir la luz, por supuesto: SQLite, para ser exactos.

Introducción a SQLite

Como se describe en su sitio Web (sqlite.org), “ SQLite es una biblioteca de software que implementa un motor de base de datos SQL independiente, sin servidor, configuración de cero, transaccional. Los elementos claves de esa instrucción giran en torno al sustantivo “ biblioteca ”. A diferencia de SQL Server, que utiliza un ensamblado de cliente para enviar solicitudes a un servidor de análisis y ejecución, SQLite reside totalmente en el proceso de cliente, lo que una base de datos “ incrustado ”. El nivel de ejecución de una base de datos SQLite durante su uso es un único archivo almacenado en el cliente de sistema de archivos y, por lo general es igualmente pequeño el tamaño de la instalación.

Por lo que la base de datos SQLite es muy variado, porque admite la mayor parte de la especificación de SQL-92, menos algunas cosas (que se describe con más detalle en el sitio Web de SQLite) como, por ejemplo, derecha y FULL OUTER JOIN, ALTER TABLE, algunos desencadenan soporte técnico, GRANT/REVOKE y escribir en las vistas. ¿Qué es impresionante es la cantidad es compatible, como las transacciones y una amplia gama de tipos de datos. Aunque es probablemente más allá de credibilidad para esperar a que un esquema de base de datos de SQL Server se portarán a SQLite sin ninguna modificación, es razonable asumir que un bastante sencillo (es decir, no aprovechar funciones o tipos específicos de SQL Server) se portarán esquema con un mínimo problemas. De esta forma ideal para escenarios donde es necesario sólo un “ ligero SQL ” SQLite.

En caso de que hay cuestiones acerca de su capacidad de aplicación o la estabilidad, SQLite está realizando lentamente su forma en una gran variedad de entornos “ ligeros ”: ya aparece en el Explorador de Mozilla Firefox (para compatibilidad con HTML 5), así como los entornos Symbian, iOS y Android, entre otros. En otras palabras, se trata cómo el “ otra mitad ” del mundo de desarrollo (es decir, no-­ centradas en Microsoft) es la base de datos ligero. SQLite sigue disfrutando de desarrollo en curso y corrección de fijación, lo que hace una apuesta bastante segura como un motor SQL mínimo.

Por supuesto, ninguna base de datos sería completo sin algún tipo de interfaz de administrador, y no desilusionó SQLite. Tiene una herramienta de línea de comandos de consola para obtener acceso y manipular las bases de datos SQLite, pero que no puede impresionar mucho todo lo que la función sysadmin. Afortunadamente, la Comunidad de código abierto tiene una serie de herramientas de SQLite (una lista larga de ellos es en el sitio Web de SQLite), pero si sólo necesita una herramienta de analizador de tipo de consulta rápida, pruebe a SQLite administrador, una herramienta gratuita que puede descargar en sqliteadmin.orbmu2k.de de .

Goin ’ nativo

SQLite se diseñó desde el principio para ser una base de datos para el desarrollador de código nativo, que es la razón por la que se implementa como un archivo DLL de C o C++ nativo. Este tipo nativo de SQLite es tanto una bendición como una maldición: reconoce, en que reduce a un lote de la carga (por ejemplo, que de atravesar la red al servidor y se devuelve de nuevo) en el tiempo total necesario para ejecutar una instrucción SQL dada; pero curse en que, debido a que la base de datos original de SQLite es un archivo DLL de C o C++ nativo, obtener al mismo desde una aplicación basada en .NET Framework de Microsoft puede suponer un reto.

Afortunadamente, el experto desarrollador de .NET Framework es consciente de que tienen acceso a un archivo DLL nativo es simplemente un ejercicio en las declaraciones P/Invoke y es relativamente fácil de crear una clase de contenedor alrededor de las declaraciones nativas en el archivo DLL SQLite. De hecho, de los conceptos básicos, como sucede con muchas cosas en la Comunidad de código abierto, se está realizado; vaya a switchonthecode.com/tutorials/csharp-tutorial-writing-a-dotnet-wrapper-for-sqlitede y se encuentra en un conjunto de trabajo de las declaraciones P/Invoke que ya establecidos, se muestra en de figura 1.

Figura 1 de declaraciones 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);
  }
}

La alta fidelidad de P/Invoke a las API de C o C++ hace que un proceso relativamente sencillo: la API de SQLite utiliza un puntero sin formato para representar la base de datos, que P/Invoke se considera como un System.IntPtr, y cada tan a menudo la API de SQLite utiliza un puntero a un int como un parámetro para que pueda modificar el contenido, se describe mediante P/Invoke con C# “ fuera ” (palabra clave). (Para obtener más información sobre el P/Invoke, consulte pinvoke.codeplex.com de ).

Llamaré al sitio Web de SQLite para la mayoría de los detalles acerca de cómo utilizar la API de SQLite, pero un rápido vistazo a cómo abrir una base de datos, ejecutar una consulta y, a continuación, cierre la base de datos le mostrará algo como de figura 2.

La figura 2 de Abrir una base de datos, ejecutar una consulta y cierre la base de datos

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);
     }
  }

Estoy Feelin ’ Mighty Low

Lo más sorprendente de esta API es que es un poco en el lado de Low nivel. Si un pirata de C++ antiguo como yo, esto puede ser algo bueno, darnos una magnífica oportunidad reminisce acerca de los días de la antigua válida, cuando hombres eran hombres, memoria se administra con la mano y las mujeres swooned en partes de cóctel a través de nuestros artículos miedos de detectar presionada untamed punteros en wilds de Windows 95... pero para el resto de estos C# whipper-snappers, estos Juan-venir-Latelys que realmente deseen realizar, de trabajo productivo es algo demasiado Low para el suelo, por así decirlo en espera. Lo que se necesita es un buena abstracción en el contenedor de dicha API para que sea más fácil de administrar y corte hacia abajo en el número de líneas de código necesario para utilizarlo.

Ajuste esta en una única clase no es difícil, especialmente porque System.Data proporciona algunas clases buena que pueden controlar la mayor parte de la interacción de la API de usuario. Que muestra los detalles completos de que es algo demasiado larga para incluir aquí la clase contenedora, denominado SQLite, pero las declaraciones que se muestra en de figura 3 proporcionan una indicación bastante clara de cómo se debe a que para se va a utilizar.

La figura 3 de declaraciones de la clase de contenedor 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);
  }

Con esta opción, a continuación, será parecido al siguiente en el ejemplo de de figura 4.

La figura 4 de utilizar la clase de contenedor 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, existen más operaciones que se ha podrían agregar a la clase de contenedor SQLite en de figura 4, pero ya tiene la funcionalidad necesaria barebones, gracias en parte a la naturaleza independiente de la base de datos maravillosa del núcleo del DataTable/DataRow y DataColumn clases en System.Data.

Abstracciones, abstracciones

En cierto modo, las ventajas de la base de datos SQLite es el diseño de Low nivel y la implementación, que se va a la fricción “ ” implicada en el uso de medios puede incrustables es bastante ligera.Agregar las clases contenedoras, asegúrese de que el archivo DLL SQLite es accesible para el programa en algún lugar (normalmente mediante la colocación en el directorio con el archivo ejecutable) y ahora escribe instrucciones SQL como un representante.Suponiendo que es lo que desea hacer, por supuesto.

Pero es probable que gran mayoría de los desarrolladores de .NET Framework están fuera de la práctica de administrar una base de datos SQL completamente “ manualmente ” a través de la API de consola, que no sabía cómo hacerlo o simplemente desea omitir ese mundo.El entorno actual de .NET Framework proporciona numerosas herramientas para crear y administrar el esquema relacional que se siente positivamente primitivo volviendo a este método manual y, lo que es más importante, improductivo.

Además, Microsoft ya ha pasado el esfuerzo de creación de una API que se describe con eficacia la mayoría de las cosas que un programador desea realizar en una base de datos relacional y se crean muchas de esas herramientas (LINQ to SQL y Entity Framework, incluso en el Diseñador de Visual Studio) de API.Por supuesto, me refiero a ADO.NET y su modelo de proveedor.No tiene la capacidad de la diapositiva SQLite “ debajo ” ADO.NET significa que toda esa coolness no está disponible para el desarrollador utiliza SQLite y que perciben como un inconveniente bastante importante.A continuación, es la solución crear un proveedor de ADO.NET para SQLite.

Como ya hemos descubierto, una de las cosas bien acerca de la Comunidad de código abierto es que hay realmente muy probable que alguien ya ha hecho todo lo que pretende para hacer, y esto no es diferente.System.Data.SQLite, puede descargar en sqlite.phxsoftware.com de , es un proveedor de ADO.NET 3.5 completo, lo que significa que todo lo que un programador puede hacer con un proveedor de la base de datos relacionales tradicionales cliente/servidor está disponible para el SQLite compatible con el desarrollador, incluido el Diseñador de Visual Studio, así como LINQ y Entity Framework.

El uso de System.Data.SQLite es bastante sencillo.Obtener la descarga (la fuente, por lo que puede crear usted mismo y escribir en el código para ver cómo funciona todo, se trata de un buen ejemplo para trabajar para obtener información sobre cómo crear un proveedor ADO.NET, si tiene curiosidad, o tomar sólo los archivos binarios si sólo desea tener acceso a “ done ” más tiempo).A continuación, colocar los bits en un lugar en el disco duro, la referencia System.Data.SQLite.dll desde el proyecto y la vida es una buena.No es sorprendente que las clases de API de live en System.Data.SQLite y una vez que se hace referencia a, puede escribir el código ADO.NET vieja buena contra la base de datos, como se muestra en de figura 5.

De la figura 5 uso 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]);
    }
  }
}

Hasta ahora, todo perfecto. Cuando se ejecuta el código de un Visual Studio 2005 o un proyecto de 2008, todo funciona sin problemas. Pero cuando se ejecuta el código de Visual Studio de 2010, un error, que afirma “ excepción no controlada: System.IO.FileLoadException: Ensamblado de modo mixto se integra con la versión de Common Language runtime, ' v2.0.50727' y no se puede cargar en el tiempo de ejecución 4.0 sin información de configuración adicionales. Un ensamblado de modo mixto, para aquellos usuarios que no ha oído hablar del término antes, es un ensamblado que contiene lenguaje intermedio de Microsoft administrado y nativo x 86 de las instrucciones de ensamblador. Esto, por supuesto, no es bueno, en dos niveles, uno, el problema obvio es necesario obtener el código para trabajar y dos, si este son un ensamblado de modo mixto, se va a crear algunos problemas al utilizar SQLite en otros entornos, como, por ejemplo, ASP.NET.

El primer problema se soluciona con facilidad mediante la adición de un archivo app.config, que indica a la 4.0 de CLR para cargar el ensamblado de modo mixto:

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

Un problema mayor que es una serie de entornos Don son compatibles con los ensamblados de modo mixto y en cualquier caso, hay un cierto aesthetic implicados aquí. Por diversas razones, es preferible una solución totalmente administrado, pero debido a que el archivo DLL SQLite es código nativo, que sería difícil. Lo que estaría bien es un puerto de la SQLite base del código para C#, se mantienen en el C original como sea posible.

Managed-todo

Una vez más, la Comunidad de código abierto proporciona cuando se le pida y, en este caso, proporciona un proyecto denominado “ C#-SQLite, ” en code.google.com/p/csharp-sqlite de . Aparentemente inició como “ ejercicio para aprender el lenguaje C# ” al trasladar el código y la wiki asociada tiene alguna explicación de lo que hizo el autor para administrar el puerto, pero el resultado es que ahora tenemos exactamente lo que se necesita: una versión de SQLite completamente administrado.

El uso de la requiere descargar las fuentes de proyecto, abrir el proyecto y lo fuera de una generación. Al igual que un número de proyectos de código abierto, C#: SQLite consta de varios proyectos, pero cada uno de ellos se incluye en su propio archivo de solución, por lo que es posible que deba abrir más de una solución. (O simplemente iniciar las generaciones desde la línea de comandos de MSBuild, lo que funciona mejor.)

Una vez que se ha creado, agregue el de C# - SQLite ensamblado (Community.C ­­ Sharp ­ SQLite) para el proyecto y de soporte técnico ADO.NET, agregue el de C# - cliente SQLite ensamblado (Community.CsharpSqlite.SQLiteClient.dll) también. Una vez más, todas las capacidades de SQLite están disponibles a través de un proveedor de ADO.NET, que se puede volver a escribir casi exacto mismo código que se muestra el anterior (consulte de figura 6).

La figura 6 mediante 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 cómo las API son casi idénticas a la versión de modo mixto de versiones anteriores (sólo han cambiado los nombres de clase y es incluso, a continuación, en realidad una pregunta de caja: Frente a “ SQLite ” “ Sqlite ” como prefijo, por ejemplo), pero ahora se obtienen todos los factores de SQLite sin los posibles problemas de seguridad (si existe) de un archivo DLL de modo nativo.

Limitaciones

A pesar de la naturaleza maravillosa de SQLite, es importante comprender sus limitaciones, si la decisión entre SQLite y SQL Server que se va a realizarse con cualquier grado de validez. SQLite no va a proporcionar todas las características de SQL Server, lejos de él. La base de datos SQLite incluso no desea que los desarrolladores que lo utilicen para utilizar varios subprocesos, mucho menos tener acceso a ella desde varios subprocesos. De hecho, es justo decir que si dos programas desean acceso a una base de datos SQLite al mismo tiempo, probablemente ha vez para actualizar a una instancia de SQL Server (Express o cualquier otro).

Áreas principales de SQLite de “ victoria ” será en muchas de las mismas áreas en que los archivos de Access que se utilizan para ocupar, con una sintaxis de SQL-92 cerca de terminar para realizar una copia, junto con una capacidad de leer los archivos de base de datos utilizados por otros entornos (Python, Perl etc.) de. Uso de los clientes de Silverlight o el teléfono también es un área muy interesante, especialmente para el almacenamiento local: seguir usando una base de datos SQLite en Silverlight el almacenamiento aislado ofrecen a los desarrolladores un portátil (que pueden viajar con el código de Silverlight) la base de datos en el que se va a almacenar los datos locales, por ejemplo. Utilizar con precaución y SQLite completa una serie de la base de datos relacional de las opciones de funcionalidad a peso.

Una vez más, si hay un tema en particular que le gustaría ver exploradas, Don dude me quitar una nota. Después de todo, se trata de su columna.

¡Feliz codificación!

Ted Neward es un principal con Neward & Associates, un independiente especializado en sistemas de plataforma de .NET Framework y Java de empresa. Ha escrito más de 100 artículos, es un MVP de C#, orador de INETA y ha sido autor o coautor de decenas de libros, incluido el próximo “Professional F# 2.0” (Wrox). Regularmente asesora y asiste como mentor. Ponerse en ted@tedneward.com de y leer su blog en blogs.tedneward.com de .