Uso de ADO.NET con Xamarin.iOS

Xamarin tiene compatibilidad integrada con la base de datos de SQLite que está disponible en iOS, expuesta mediante ADO conocido. Sintaxis similar a NET. El uso de estas API requiere que escriba instrucciones SQL procesadas por SQLite, como CREATE TABLE, INSERT e instrucciones SELECT.

Referencias de ensamblado

Para usar el acceso a SQLite a través de ADO.NET debe agregar System.Data y Mono.Data.Sqlite referencias al proyecto de iOS, como se muestra aquí (para ejemplos en Visual Studio para Mac y Visual Studio):

Haga clic con el botón derecho en Referencias > Editar referencias..., a continuación, haga clic para seleccionar los ensamblados necesarios.

Acerca de Mono.Data.Sqlite

Usaremos la clase Mono.Data.Sqlite.SqliteConnection para crear un archivo de base de datos en blanco y, a continuación, crearemos instancias de SqliteCommand objetos que podemos usar para ejecutar instrucciones SQL en la base de datos.

  1. Crear una base de datos en blanco: llamar al método CreateFile con una ruta de acceso de archivo válida (es decir, que se pueda escribir). Debe comprobar si el archivo ya existe antes de llamar a este método; de lo contrario, se creará una nueva base de datos (en blanco) sobre la parte superior de la antigua y se perderán los datos del archivo anterior:

    Mono.Data.Sqlite.SqliteConnection.CreateFile (dbPath);

    Nota:

    La variable dbPath debe determinarse según las reglas descritas anteriormente en este documento.

  2. Crear una conexión de base de datos: una vez creado el archivo de base de datos de SQLite, puede crear un objeto de conexión para acceder a los datos. La conexión se construye con una cadena de conexión que adopta la forma de Data Source=file_path, como se muestra aquí:

    var connection = new SqliteConnection ("Data Source=" + dbPath);
    connection.Open();
    // do stuff
    connection.Close();
    

    Como se mencionó anteriormente, nunca se debe volver a usar una conexión en distintos subprocesos. En caso de duda, cree la conexión según sea necesario y ciérrela cuando haya terminado; pero esté alerta si tiene que hacerlo con más frecuencia de lo necesario.

  3. Crear y ejecutar un comando de base de datos: una vez que tenemos una conexión, podemos ejecutar comandos SQL arbitrarios en él. El código siguiente muestra una instrucción CREATE TABLE que se ejecuta.

    using (var command = connection.CreateCommand ()) {
        command.CommandText = "CREATE TABLE [Items] ([_id] int, [Symbol] ntext, [Name] ntext);";
        var rowcount = command.ExecuteNonQuery ();
    }
    

Al ejecutar SQL directamente en la base de datos, debe tomar las precauciones normales para no realizar solicitudes no válidas, como intentar crear una tabla que ya existe. Realice un seguimiento de la estructura de la base de datos para que no se produzca una excepción SqliteException como “Tabla de errores de SQLite [Items] ya existe”.

Acceso a datos básicos

El código de ejemplo DataAccess_Basic para este documento tiene este aspecto al ejecutarse en iOS:

iOS ADO.NET sample

El código siguiente muestra cómo realizar operaciones simples de SQLite y muestra los resultados como texto en la ventana principal de la aplicación.

Deberá incluir estos espacios de nombres:

using System;
using System.IO;
using Mono.Data.Sqlite;

En el ejemplo de código siguiente se muestra una interacción completa de la base de datos:

  1. Creación del archivo de base de datos
  2. Inserción de algunos datos
  3. Consultas de datos

Estas operaciones normalmente aparecen en varios lugares en todo el código, por ejemplo, puede crear el archivo de base de datos y las tablas cuando la aplicación se inicie por primera vez y realice lecturas y escrituras de datos en pantallas individuales de la aplicación. En el ejemplo siguiente se han agrupado en un único método para este ejemplo:

public static SqliteConnection connection;
public static string DoSomeDataAccess ()
{
    // determine the path for the database file
    string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "adodemo.db3");

    bool exists = File.Exists (dbPath);

    if (!exists) {
        Console.WriteLine("Creating database");
        // Need to create the database before seeding it with some data
        Mono.Data.Sqlite.SqliteConnection.CreateFile (dbPath);
        connection = new SqliteConnection ("Data Source=" + dbPath);

        var commands = new[] {
            "CREATE TABLE [Items] (_id ntext, Symbol ntext);",
            "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('1', 'AAPL')",
            "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('2', 'GOOG')",
            "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('3', 'MSFT')"
        };
        // Open the database connection and create table with data
        connection.Open ();
        foreach (var command in commands) {
            using (var c = connection.CreateCommand ()) {
                c.CommandText = command;
                var rowcount = c.ExecuteNonQuery ();
                Console.WriteLine("\tExecuted " + command);
            }
        }
    } else {
        Console.WriteLine("Database already exists");
        // Open connection to existing database file
        connection = new SqliteConnection ("Data Source=" + dbPath);
        connection.Open ();
    }

    // query the database to prove data was inserted!
    using (var contents = connection.CreateCommand ()) {
        contents.CommandText = "SELECT [_id], [Symbol] from [Items]";
        var r = contents.ExecuteReader ();
        Console.WriteLine("Reading data");
        while (r.Read ())
            Console.WriteLine("\tKey={0}; Value={1}",
                              r ["_id"].ToString (),
                              r ["Symbol"].ToString ());
    }
    connection.Close ();
}

Consultas más complejas

Dado que SQLite permite ejecutar comandos SQL arbitrarios en los datos, puede realizar cualquier instrucción CREATE, INSERT, UPDATE, DELETE o SELECT que desee. Puede leer sobre los comandos SQL compatibles con SQLite en el sitio web de Sqlite. Las instrucciones SQL se ejecutan mediante uno de los tres métodos de un objeto SqliteCommand:

  • ExecuteNonQuery: se usa normalmente para la creación de tablas o la inserción de datos. El valor devuelto de algunas operaciones es el número de filas afectadas; de lo contrario, es -1.
  • ExecuteReader: se usa cuando se debe devolver una colección de filas como un SqlDataReader.
  • ExecuteScalar: recupera un único valor (por ejemplo, un agregado).

EXECUTENONQUERY

Las instrucciones INSERT, UPDATE y DELETE devolverán el número de filas afectadas. Todas las demás instrucciones SQL devolverán -1.

using (var c = connection.CreateCommand ()) {
    c.CommandText = "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('1', 'APPL')";
    var rowcount = c.ExecuteNonQuery (); // rowcount will be 1
}

EXECUTEREADER

El método siguiente muestra una cláusula WHERE en la instrucción SELECT. Dado que el código está diseñando una instrucción SQL completa, debe tener cuidado de escapar los caracteres reservados, como la comilla (') alrededor de las cadenas.

public static string MoreComplexQuery ()
{
    var output = "";
    output += "\nComplex query example: ";
    string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal), "ormdemo.db3");

    connection = new SqliteConnection ("Data Source=" + dbPath);
    connection.Open ();
    using (var contents = connection.CreateCommand ()) {
        contents.CommandText = "SELECT * FROM [Items] WHERE Symbol = 'MSFT'";
        var r = contents.ExecuteReader ();
        output += "\nReading data";
        while (r.Read ())
            output += String.Format ("\n\tKey={0}; Value={1}",
                    r ["_id"].ToString (),
                    r ["Symbol"].ToString ());
    }
    connection.Close ();

    return output;
}

El método ExecuteReader devuelve un objeto SqliteDataReader. Además del método Read que se muestra en el ejemplo, otras propiedades útiles incluyen:

  • RowsAffected: recuento de filas afectadas por la consulta.
  • HasRows: indica si se devolvieron filas.

EXECUTESCALAR

Úselo para instrucciones SELECT que devuelven un valor único (por ejemplo, un agregado).

using (var contents = connection.CreateCommand ()) {
    contents.CommandText = "SELECT COUNT(*) FROM [Items] WHERE Symbol <> 'MSFT'";
    var i = contents.ExecuteScalar ();
}

El tipo de valor devuelto del método ExecuteScalar es object: debe convertir el resultado en función de la consulta de base de datos. El resultado podría ser un entero de una consulta COUNT o una cadena de una sola columna SELECT. Tenga en cuenta que esto es diferente a otros métodos Execute que devuelven un objeto lector o un recuento del número de filas afectadas.

Microsoft.Data.Sqlite

Hay otra biblioteca Microsoft.Data.Sqlite, que se puede instalar desde NuGet, que es funcionalmente equivalente a Mono.Data.Sqlite y permite los mismos tipos de consultas.

Hay una comparación entre las dos bibliotecas y algunos detalles específicos de Xamarin. Lo más importante para las aplicaciones de Xamarin.iOS, debe incluir una llamada de inicialización:

// required for Xamarin.iOS
SQLitePCL.Batteries_V2.Init();