Uso de SQLite.NET con Android

La biblioteca de SQLite.NET que recomienda Xamarin es un ORM muy básico que le permite almacenar y recuperar fácilmente objetos en la base de datos local de SQLite en un dispositivo Android. ORM significa Asignación relacional de objetos: una API que permite guardar y recuperar "objetos" de una base de datos sin escribir instrucciones SQL.

Para incluir la biblioteca de SQLite.NET en una aplicación de Xamarin, agregue el siguiente paquete NuGet al proyecto:

Paquete SQLite.NET NuGet

Hay una serie de paquetes SQLite diferentes disponibles: asegúrese de elegir el correcto (es posible que no sea el resultado superior en la búsqueda).

Importante

SQLite.NET es una biblioteca de terceros compatible con el repositorio praeclarum/sqlite-net.

Una vez que tenga la biblioteca de SQLite.NET disponible, siga estos tres pasos para usarlo para acceder a una base de datos:

  1. Agregar una instrucción using: Agregue la siguiente instrucción a los archivos de C# donde se requiere acceso a los datos:

    using SQLite;
    
  2. Crear una base de datos en blanco: Se puede crear una referencia de base de datos pasando la ruta de acceso del archivo al constructor de clase SQLiteConnection. No es necesario comprobar si el archivo ya existe: se creará automáticamente si es necesario; de lo contrario, se abrirá el archivo de base de datos existente. La variable dbPath debe determinarse según las reglas descritas anteriormente en este documento:

    var db = new SQLiteConnection (dbPath);
    
  3. Guardar datos: Una vez creado un objeto SQLiteConnection, los comandos de base de datos se ejecutan llamando a sus métodos, como CreateTable e Insert como este:

    db.CreateTable<Stock> ();
    db.Insert (newStock); // after creating the newStock object
    
  4. Recuperar datos: Para recuperar un objeto (o una lista de objetos) use la sintaxis siguiente:

    var stock = db.Get<Stock>(5); // primary key id of 5
    var stockList = db.Table<Stock>();
    

Ejemplo de acceso a datos básicos

El DataAccess_Basic código de ejemplo de este documento tiene este aspecto cuando se ejecuta en Android. El código muestra cómo realizar operaciones de SQLite.NET simples y muestra los resultados como texto en la ventana principal de la aplicación.

Android

Ejemplo de SQLite.NET de Android

En el ejemplo de código siguiente se muestra una interacción completa de la base de datos mediante la biblioteca de SQLite.NET para encapsular el acceso a la base de datos subyacente. Muestra lo siguiente:

  1. Creación del archivo de base de datos

  2. Insertar algunos datos mediante la creación de objetos y, a continuación, guardarlos

  3. Consultas de datos

Deberá incluir estos espacios de nombres:

using SQLite; // from the github SQLite.cs class

El último requiere que haya agregado SQLite al proyecto. Tenga en cuenta que la tabla de base de datos SQLite se define agregando atributos a una clase (la clase Stock ) en lugar de un comando CREATE TABLE.

[Table("Items")]
public class Stock {
    [PrimaryKey, AutoIncrement, Column("_id")]
    public int Id { get; set; }
    [MaxLength(8)]
    public string Symbol { get; set; }
}
public static void DoSomeDataAccess () {
       Console.WriteLine ("Creating database, if it doesn't already exist");
   string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "ormdemo.db3");
   var db = new SQLiteConnection (dbPath);
   db.CreateTable<Stock> ();
   if (db.Table<Stock> ().Count() == 0) {
        // only insert the data if it doesn't already exist
        var newStock = new Stock ();
        newStock.Symbol = "AAPL";
        db.Insert (newStock);
        newStock = new Stock ();
        newStock.Symbol = "GOOG";
        db.Insert (newStock);
        newStock = new Stock ();
        newStock.Symbol = "MSFT";
        db.Insert (newStock);
    }
    Console.WriteLine("Reading data");
    var table = db.Table<Stock> ();
    foreach (var s in table) {
        Console.WriteLine (s.Id + " " + s.Symbol);
    }
}

El uso del atributo [Table] sin especificar un parámetro de nombre de tabla hará que la tabla de base de datos subyacente tenga el mismo nombre que la clase (en este caso, "Stock"). El nombre de tabla real es importante si escribe consultas SQL directamente en la base de datos en lugar de usar los métodos de acceso a datos ORM. Del mismo modo, el atributo [Column("_id")] es opcional y, si no hay una columna, se agregará a la tabla con el mismo nombre que la propiedad de la clase.

Atributos de SQLite

Los atributos comunes que puede aplicar a las clases para controlar cómo se almacenan en la base de datos subyacente incluyen:

  • [PrimaryKey]: Este atributo se puede aplicar a una propiedad entera para forzar que sea la clave principal de la tabla subyacente. No se admiten claves principales compuestas.

  • [AutoIncrement]: Este atributo hará que el valor de una propiedad entera se incremente automáticamente para cada nuevo objeto insertado en la base de datos.

  • [Column(name)]: El parámetro name establece el nombre de la columna de base de datos subyacente.

  • [Table(name)]: Marca la clase como capaz de almacenarse en una tabla de SQLite subyacente con el nombre especificado.

  • [MaxLength(value)]: Restringir la longitud de una propiedad de texto, cuando se intenta insertar una base de datos. El consumo de código debe validar esto antes de insertar el objeto, ya que este atributo solo se "comprueba" cuando se intenta insertar o actualizar una base de datos.

  • [Omitir]: Hace que SQLite.NET omita esta propiedad. Esto es especialmente útil para las propiedades que tienen un tipo que no se puede almacenar en la base de datos, o propiedades que modelan colecciones que SQLite no puede resolver automáticamente.

  • [Único]: Garantiza que los valores de la columna de base de datos subyacente sean únicos.

La mayoría de estos atributos son opcionales. Siempre debe especificar una clave principal de entero para que las consultas de selección y eliminación se puedan realizar de forma eficaz en los datos.

Consultas más complejas

Los métodos siguientes en SQLiteConnection se pueden usar para realizar otras operaciones de datos:

  • Insertar: Agrega un nuevo objeto a la base de datos.

  • Obtener<T>: Intenta recuperar un objeto mediante la clave principal.

  • Tabla<T>: Devuelve todos los objetos de la tabla.

  • Eliminar: Elimina un objeto mediante su clave principal.

  • Consulta<T>: Realizar una consulta SQL que devuelve una serie de filas (como objetos).

  • Ejecutar: Use este método (y no Query) cuando no espere filas de nuevo desde SQL (como instrucciones INSERT, UPDATE y DELETE).

Obtención de un objeto mediante la clave principal

SQLite.Net proporciona el método Get para recuperar un único objeto en función de su clave principal.

var existingItem = db.Get<Stock>(3);

Selección de un objeto mediante Linq

Los métodos que devuelven colecciones admiten IEnumerable<T> para poder usar Linq para consultar o ordenar el contenido de una tabla. En el código siguiente se muestra un ejemplo con Linq para filtrar todas las entradas que comienzan por la letra "A":

var apple = from s in db.Table<Stock>()
    where s.Symbol.StartsWith ("A")
    select s;
Console.WriteLine ("-> " + apple.FirstOrDefault ().Symbol);

Selección de un objeto mediante SQL

Aunque SQLite.Net puede proporcionar acceso basado en objetos a los datos, a veces es posible que tenga que realizar una consulta más compleja de la que permite Linq (o puede que necesite un rendimiento más rápido). Puede usar comandos SQL con el método Consulta, como se muestra aquí:

var stocksStartingWithA = db.Query<Stock>("SELECT * FROM Items WHERE Symbol = ?", "A");
foreach (var s in stocksStartingWithA) {
    Console.WriteLine ("a " + s.Symbol);
}

Nota:

Al escribir instrucciones SQL directamente, se crea una dependencia de los nombres de las tablas y columnas de la base de datos, que se han generado a partir de las clases y sus atributos. Si cambia esos nombres en el código, debe recordar actualizar las instrucciones SQL escritas manualmente.

Eliminación de un objeto

La clave principal se usa para eliminar la fila, como se muestra aquí:

var rowcount = db.Delete<Stock>(someStock.Id); // Id is the primary key

Puede comprobar el rowcount para confirmar el número de filas afectadas (eliminadas en este caso).

Uso de SQLite.NET con varios subprocesos

SQLite admite tres modos de subproceso diferentes: Subproceso único, Multiproceso, y Serializado. Si desea acceder a la base de datos desde varios subprocesos sin restricciones, puede configurar SQLite para usar el modo de subproceso Serializado. Es importante establecer este modo al principio de la aplicación (por ejemplo, al principio del método OnCreate).

Para cambiar el modo de subproceso, llame a SqliteConnection.SetConfig. Por ejemplo, esta línea de código configura SQLite para modo Serializado:

using using Mono.Data.Sqlite;
...
SqliteConnection.SetConfig(SQLiteConfig.Serialized);

La versión de Android de SQLite tiene una limitación que requiere algunos pasos más. Si la llamada a SqliteConnection.SetConfig genera una excepción de SQLite como library used incorrectly, debe usar la siguiente solución alternativa:

  1. Vincule a la biblioteca nativa de libsqlite.so para que las API de sqlite3_shutdown y sqlite3_initialize estén disponibles para la aplicación:

    [DllImport("libsqlite.so")]
    internal static extern int sqlite3_shutdown();
    
    [DllImport("libsqlite.so")]
    internal static extern int sqlite3_initialize();
    
  2. Al principio del método OnCreate, agregue este código para apagar SQLite, configúrelo para modo Serializado y reinicialice SQLite:

    using using Mono.Data.Sqlite;
    ...
    sqlite3_shutdown();
    SqliteConnection.SetConfig(SQLiteConfig.Serialized);
    sqlite3_initialize();
    

Esta solución alternativa también funciona para la biblioteca de Mono.Data.Sqlite. Para obtener más información sobre SQLite y multiproceso, vea SQLite y varios subprocesos.