Armazenar dados localmente com SQLite

Concluído

O SQLite é útil quando você tem dados relacionais. Suponha que você esteja compilando um aplicativo de rede social. Você precisa armazenar informações sobre assinantes no aplicativo. Esses dados incluem uma ID exclusiva para cada usuário e seus nomes. Você pode facilmente modelar esse tipo de relação em um banco de dados SQLite.

Nesta unidade, você aprenderá a usar o SQLite em um aplicativo .NET MAUI usando o SQLite-net.

O que é o SQLite?

O SQLite é um banco de dados local multiplataforma leve que se tornou um padrão da indústria para aplicativos móveis. O SQLite não requer um servidor. O banco de dados é armazenado em um arquivo de disco no sistema de arquivos do dispositivo. Todas as operações de leitura e de gravação são executadas diretamente no arquivo de disco do SQLite.

As bibliotecas nativas do SQLite estão compiladas no Android e no iOS por padrão, no entanto, o mecanismo suporta apenas uma API C/C++. Esse cenário não é ideal para desenvolvedores .NET que querem que o SQLite e o .NET interajam de alguma forma.

O que é o SQLite-net?

Há vários wrappers C# ao redor do mecanismo do SQLite nativo que os desenvolvedores do .NET podem usar. Muitos desenvolvedores do .NET usam um wrapper C# popular chamado SQLite-net.

O SQLite-net é um mapeador relacional de objetos. Ele ajuda a simplificar o processo de definir esquemas de banco de dados deixando que você use os modelos que são definidos nos seus projetos para servir como esquema.

Diagram showing how SQLite-net provides a .NET wrapper and the SQLite C/C++ engine.

Por exemplo, considere a seguinte classe que modela um User:

class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    ...
}

Ao usar o mapeador relacional de objetos, você pode usar essa classe User inicial e criar uma tabela de banco de dados chamada User que tem colunas para os campos Id e Username nessa classe.

O SQLite-net é enviado como um pacote do NuGet. É necessário adicionar o pacote sqlite-net-pcl aos aplicativos para usá-lo. Use o Gerenciador de pacotes do NuGet no Visual Studio. Além disso, se você quiser executar um aplicativo no Android, também precisará adicionar o pacote SQLitePCLRaw.provider.dynamic_cdecl.

Como conectar-se a um banco de dados SQLite

Você pode estabelecer uma conexão com um banco de dados SQLite a partir de um aplicativo por meio de um objeto SQLiteConnection. Essa classe é definida no namespace SQLite, juntamente com os outros tipos e métodos que o SQLite fornece. Quando você criar uma instância desse objeto, passe o nome do arquivo para o arquivo de banco de dados. Em seguida, o construtor abrirá o arquivo se ele existir ou o criará se ele não estiver presente.

O código a seguir mostra um exemplo:

using SQLite;
...
string filename = ...
SQLiteConnection conn = new SQLiteConnection(filename);

Lembre-se de que filename precisa apontar para um local na área restrita do aplicativo.

Como criar uma tabela

Lembre-se de que o SQLite-net é um mapeador relacional de objeto, o que significa que é possível criar o esquema de banco de dados com base em classes C#. O SQLite-net pode compilar uma tabela de banco de dados com uma classe C# comum, mas há muitos atributos que você pode adicionar a uma classe para fornecer metadados adicionais. Esses metadados ajudam o SQLite a impor recursos como exclusividade e aplicar restrições aos seus dados.

Os atributos disponíveis incluem:

  • Table: Especifique o nome da tabela se desejar que seja diferente do nome da classe
  • PrimaryKey: Especifique uma coluna como chave primária
  • AutoIncrement: Especifique que uma coluna deve aumentar automaticamente em valor quando uma nova linha for inserida.
  • Column: Especifique o nome de uma coluna se desejar que seja diferente do nome da propriedade
  • MaxLength: Especifique o número máximo de caracteres que podem ser usados na coluna
  • Unique: Especifique que o valor na coluna deve ser diferente de todas as outras linhas

O seguinte código mostra uma versão atualizada da classe User que aplica esses atributos:

[Table("user")]
public class User
{
    // PrimaryKey is typically numeric 
    [PrimaryKey, AutoIncrement, Column("_id")]
    public int Id { get; set; }

    [MaxLength(250), Unique]
    public string Username { get; set; }
    ...
}

Depois de definir sua classe C#, chame o método genérico CreateTable na classe SQLiteConnection para gerar a tabela no banco de dados. Especifique a classe como o parâmetro de tipo. Veja um exemplo:

SQLiteConnection conn = new SQLiteConnection(filename);
conn.CreateTable<User>();

Se a tabela já existir no banco de dados, o método CreateTable verificará o esquema para ver se há alguma alteração nele. Se houver, a operação tentará atualizar o esquema de banco de dados.

Como realizar operações básicas de leitura e de gravação

Depois de criar uma tabela, você pode começar a interagir com ela. Para adicionar uma linha, use o método Insert na instância SQLiteConnection e forneça um objeto do tipo apropriado que contém os dados a serem inseridos. O seguinte código mostra como adicionar uma linha à tabela User:

public int AddNewUser(User user)
{
    int result = conn.Insert(user);
    return result;
}

O método Insert retorna um int, que representa o número de linhas inseridas na tabela. Neste caso, esse número é um.

Para recuperar linhas de uma tabela, use o método Table. Esse método retorna uma coleção de objetos (que pode estar vazia):

public List<User> GetAllUsers()
{
    List<User> users = conn.Table<User>().ToList();
    return users;
}

O método Table retorna um objeto TableQuery\<T>. Para obter uma List, use o método ToList, conforme mostrado no exemplo anterior.

Executar uma consulta SQLite usando LINQ

O método Table recupera todas as linhas de uma tabela. Na maioria dos casos, retorne apenas um subconjunto das linhas que correspondem a um conjunto de critérios especificados. Nessas tarefas, use o LINQ com o SQLite-net.

O SQLite-net dá suporte a muitas consultas LINQ comuns incluindo:

  • Where
  • Take
  • Ignorar
  • OrderBy
  • OrderByDescending
  • ThenBy
  • ElementAt
  • Primeiro
  • FirstOrDefault
  • ThenByDescending
  • Contagem

Com esses métodos, é possível usar a sintaxe do método de extensão ou a sintaxe de C# do LINQ. Por exemplo, veja o seguinte snippet de código que permite recuperar os detalhes de um usuário especificado:

public User GetByUsername(string username)
{
    var user = from u in conn.Table<User>()
               where u.Username == username
               select u;
    return user.FirstOrDefault();
}

Atualizar e excluir linhas

Você atualiza uma linha usando o método SQLiteConnection do objeto Update. Você fornece um objeto que define a linha a ser atualizada com os novos valores. O método Update modifica a linha que tem o mesmo valor de chave primária que o objeto fornecido. O valor retornado é o número de linhas alteradas. Quando esse valor é zero, isso significa que nenhuma linha com uma chave primária correspondente foi encontrada e nada foi atualizado. O próximo snippet mostra esse método em ação:

public int UpdateUser(User user)
{
    int result = 0;
    result = conn.Update(user);
    return result;
}

Remova linhas de uma tabela com o método SQLiteConnection do objeto Delete. A forma mais simples desse método usa a chave primária do item a ser excluído como o parâmetro, conforme mostrado no seguinte exemplo. Essa forma do método Delete é genérica e requer um parâmetro de tipo genérico. O valor retornado é o número de linhas removidas da tabela:

public int DeleteUser(int userID)
{
    int result = 0;
    result = conn.Delete<User>(userID);
    return result;
}