Introducción

Cuando hacemos una aplicación lo más normal es que necesitemos de alguna manera almacenar datos en local. Tenemos muchas alternativas para hacerlo (algunas mejores y otras peores).

Una de las opciones es almacenar los datos utilizando una base de datos relacional como SQLite. Esta librería está escrita en C/C++ y eso nos da algunos problemas a la hora de trabajar en aplicaciones universales, pero en este post veremos cómo solucionarlo.

Instalar y configurar SQLite

Antes de ponernos a escribir código debemos instalar la librería de SQLite. Como hemos comentado antes, SQLite está desarrollado en C/C++, así que tenemos que añadir las referencias al Runtime de C++.

El primer paso es instalar una extensión para Visual Studio de SQLite. Se instalan por separado la de Windows Phone 8.1 y la de Windows 8.1. Un vez lo hemos instalado debemos referenciarlos en los dos proyectos junto con el Runtime de C++ para cada plataforma.

extension

Importante: Al añadir la extensión al Runtime de C++ ya no podremos compilar para Any CPU, deberemos compilar de forma separada para cada arquitectura.

Código de ejemplo

Hemos dejado en GitHub el código de la aplicación de Windows 8 que hemos utilizado para escribir el post. Es una aplicación muy sencilla que nos permite añadir webs a un listado, eliminarlas y filtrarlas.

app

Trabajando con SQLite

Una vez tenemos todo configurado vamos a ver cómo trabajar con SQLite desde una Universal App. Todo el código lo podemos meter en el proyecto Shared o en una Portable Class Library (PCL) porque lo podemos compartir al 100% entre las dos plataformas.

Nota: En el ejemplo que vamos a ver solo hemos añadido el proyecto de Windows 8 para simplificar la demo.

En NuGet tenemos disponible SQLite.Net, una librería para trabajar de forma más sencilla con SQLite en Universal Apps. Disponemos de la versión síncrona y asíncrona. Lo instalamos y ya podemos empezar con el desarrollo.

nuget

Nota: Para poder utilizar SQLite.Net en el proyecto Shared tenemos que instalar el paquete de NuGet tanto en el proyecto de Windows 8.1 como en el de Windows Phone 8.1.

Modelos

En este ejemplo vamos a crear una lista de webs. Para ello vamos a almacenar el nombre y la URL. Tendremos la siguiente clase para las webs.

[code language=”csharp”]
public class Web
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }

[Unique, NotNull]
public string Name { get; set; }

[NotNull]
public string Url { get; set; }
}

[/code]

En nuestro caso hemos marcado la propiedad Id de nuestra clase como clave primaria y la hemos definido como autoincremental. Además hemos añadido que tanto el nombre como la Url no puede ser nula y además que el nombre debe ser único.

Conexión a la base de datos

Para simplificar la demo hemos añadido todo el código de acceso a datos en un servicio llamado SQLiteService.

[code language=”csharp”]
#region Fields
private readonly SQLiteAsyncConnection sqliteConnection;
private readonly string dataBaseName = "example.db";
#endregion
public SQLiteService()
{
sqliteConnection = new SQLiteAsyncConnection(() =>
{
return new SQLiteConnectionWithLock(new SQLitePlatformWinRT(), new SQLiteConnectionString(
Path.Combine(ApplicationData.Current.LocalFolder.Path, dataBaseName), false));
});
}

[/code]

Lo primero que debemos hacer para poder tener acceso a trabajar con la base de datos es crear la conexión. En nuestro caso hemos utilizado la conexión asíncrona (SQLiteAsyncConnection).

El constructor de la conexión nos pide un método que devuelva un SQLiteConnectionWithLock. Para crear la instancia de SQLiteConnectionWithLock debemos pasarle la plataforma (de tipo ISQLitePlatform) y una cadena de conexión.

Con SQLite.Net tenemos disponible la clase SQLitePlatformWinRT, que es la ISQLitePlatform que necesitamos para trabajar con Universal Apps.

Además la cadena de conexión debe referenciar al archivo de la aplicación donde se va a crear la base de datos de SQLite. También le indicamos con el false que no queremos que almacenen los DateTime como Ticks.

Crear la conexión de la base de datos es lo más difícil de trabajar con SQLite, pero no debería darnos muchos problemas ¡Ahora todo es coser y programar!

Inicializar la Base de datos

Ahora que ya tenemos la conexión a la base de datos la podemos utilizar para crear las tablas.

[code language=”csharp”]
public async Task InitializeDatabaseAsync()
{
await sqliteConnection.CreateTableAsync<Web>();
}

[/code]

Es tan simple como llamar al método CreateTableAsync de nuestra conexión. En el caso de haber elegido hacer la librería que trabaja síncronamente sería exactamente igual pero de forma síncrona.

Para hacernos la tarea más sencilla podemos crear varias tablas a la vez con cualquiera de las siguientes instrucciones:

[code language=”csharp”]
await sqliteConnection.CreateTablesAsync<T1, T2, T3, T4>();
await sqliteConnection.CreateTablesAsync(T1, T2, T3, T4);

[/code]

En el código de ejemplo llamamos en cada ejecución de la aplicación al método InitializeDatabaseAsync que crea las tablas de nuestra aplicación si no estuvieran creadas.

[code language=”csharp”]
await sqliteService.InitializeDatabaseAsync();
[/code]

Además disponemos de un método para eliminar tablas.

[code language=”csharp”]
await sqliteConnection.DropTableAsync<Web>();
[/code]

Acceso a datos

Ahora ya lo tenemos todo preparado para poder trabajar con los datos. Como vamos a ver trabajar con SQLite para acceder y editar los datos es súper sencillo.

Aquí tenemos el método de inserción de una nueva Web en la aplicación.

[code language=”csharp”]
public Task<int> CreateWebAsync(Web item)
{
return sqliteConnection.InsertAsync(item);
}

[/code]

Como veis es muy sencillo. Además si intentamos meter dos webs con el mismo nombre nos saltará una excepción ya que hemos indicado en la clase Web que el nombre debe ser único. Por eso en el ViewModel controlamos esa posible excepción.

[code language=”csharp”]
try
{
var web = new Web() { Name = WebName, Url = WebUrl.ToUrl()};
await sqliteService.CreateWebAsync(web);

}
catch (SQLiteException ex)
{
MessageDialog dialog = new MessageDialog(ex.Message, "Error to add web");
var dialogTask = dialog.ShowAsync();
}

[/code]

Nota: La inserción modifica la entidad después de insertarla. Gracias a esto cuando tenemos en nuestro modelo un campo autoincremental tras la inserción dispondremos del valor que se le ha asignado. Es decir, dispondremos de la entidad tal y como aparece en nuestra base de datos.

Si queremos obtener todas las webs de nuestra base de datos simplemente tenemos que hacer un ToList de la tabla Web.

[code language=”csharp”]
public Task<List<Web>> GetAllWebsAsync()
{
return sqliteConnection.Table<Web>().ToListAsync();
}
[/code]

Trabajar directamente con la Tabla (AsyncTableQuery) nos permite realizar muchas acciones para obtener los datos que queremos y de la forma que queremos.

linq

Así pues podemos hacer cosas como buscar en una tabla.

[code language=”csharp”]
public Task<List<Web>> FindWebAsync(Expression<Func<Web, bool>> predicate)
{
return sqliteConnection.Table<Web>().Where(predicate).ToListAsync();
}

[/code]

Hemos creado una función a la que le pasamos un predicado que será el que aplicará en el Where de la tabla para filtrar las webs.

En la aplicación lo utilizamos para buscar por nombre.

[code language=”csharp”]
var list = await sqliteService.FindWebAsync(web => web.Name.ToLower().Contains(Query.ToLower()));
[/code]

Actualizar una web o eliminarla es así de sencillo.

[code language=”csharp”]
public Task<int> UpdateWebAsync(Web item)
{
return sqliteConnection.UpdateAsync(item);
}

public Task<int> DeleteWebAsync(Web item)
{
return sqliteConnection.DeleteAsync(item);
}

[/code]

Como veis utilizamos los métodos correspondientes que nos ofrece la conexión.

Conclusión

Como hemos podido ver trabajar con SQLite es muy sencillo. Si bien lleva un poco de trabajo instalarlo y configurarlo, a la hora de trabajar nos ahorra mucho tiempo y errores.

Tenemos que tener en cuenta que estamos trabajando con una base de datos relacional, por tanto estamos disfrutando de toda la potencia y robustez de una base de datos en nuestras aplicaciones.

En mi caso la mayoría de datos que mis ‘aplicaciones for fun’ almacenan lo hacen en ficheros de texto con los datos serializado en JSON. Esto en muchos casos puede dar muchos problemas que se resuelven con la robustez de usar una base de datos.

Otra ventaja de SQLite es que es multiplataforma. Aquí tenéis un post de cómo utilizarlo para trabajar en Xamarin.

Y tú ¿Cómo almacenas datos locales en tus aplicaciones? ¿Usas alguna otra tecnología similar a SQLite? ¡Déjalo en los comentarios! J

Un saludo y nos vemos en el futuro.

Sergio Gallardo Sales (@maktub82)

Recursos

Código de ejemplo – https://github.com/maktub82/sqlite-example

SQLite.Net – https://github.com/oysteinkrog/SQLite.Net-PCL

SQLite for Windows Phone 8.1 –https://visualstudiogallery.msdn.microsoft.com/5d97faf6-39e3-4048-a0bc-adde2af75d1b

SQLite for Windows Runtime (Windows 8.1) –

https://visualstudiogallery.msdn.microsoft.com/1d04f82f-2fe9-4727-a2f9-a2db127ddc9a

Usando SQLite en desarrollos multiplataforma –

http://blogs.msdn.com/b/esmsdn/archive/2015/02/13/usando-sqlite-en-desarrollos-multiplataforma.aspx