.NET
MAUI - Usando o banco de dados SQLite - I
![]() |
Neste artigo vamos criar uma aplicação .NET MAUI para gerenciar um pequena biblioteca de livros pessoal realizando a persistência dos dados no SQLite. |
As aplicações .NET MAUI podem acessar uma variedade de bancos de dados e isso inclui bancos de dados relacionais (MySQL, Oracle, PostgreSQL) e bancos de dados NoSQL (MongoDB). A escolha do banco de dados a ser usado vai depender das necessidades específicas do seu aplicativo e da plataforma de destino.
Em se tratando de banco de dados locais O SQLite é uma escolha popular para aplicativos móveis multiplataforma, incluindo aplicativos .NET MAUI. Ele é incorporado diretamente na biblioteca .NET MAUI e permite que você crie bancos de dados locais leves para armazenar dados offline ou dados que não precisam ser compartilhados com um servidor.
O
SQLite é um sistema de banco de dados local leve que se tornou um padrão da
indústria para aplicativos móveis.
Ele é compacto, eficiente, altamente confiável, de código aberto e muito popular
para aplicativos que precisam de um banco de dados leve e autônomo.
O
SQLite é uma biblioteca em processo que implementa um mecanismo de banco
de dados SQL transacional independente, sem servidor e sem configuração e usa o
sistema de arquivos local para armazenamento. (Ao eliminar o servidor, o
SQLite elimina a complexidade.)
Assim, não há necessidade de suporte multitarefa ou comunicação entre processos
do sistema operacional e o SQLite requer apenas leitura/gravação em algum
armazenamento.
A
biblioteca SQLite foi escrita originalmente em C e C++ e pode ser acessada
através de um invólucro C#
onde podemos usar todos o conhecimento do C# para criar aplicações baseadas no
SQLite. Este invólucro C# refere-se à biblioteca nuget
SQLite-net e suas dependências que fornecem
um mecanismo para mapear classes para tabelas e assim podemos trabalhar com
objetos na memória fazendo a correspondência com as tabelas ou seja podemos
fazer o mapeamento ORM - Object Relational Mapping.
Neste
artigo vamos criar uma aplicação .NET MAUI para gerenciar informações de
uma pequena biblioteca pessoal e realizar o CRUD
básico em informações sobre livros representando pela entidade Livro onde vamos
usar o banco de dados SQLite.
recursos usados:
VS 2022
SQLite
.NET MAUI
Criando o projeto
No VS 2022 Community vamos criar um projeto usando o template .NET MAUI e nomear a solução como MeuLivrosApp e o projeto como MeuLivros.
Após criar o projeto vamos remover as referências que não vamos usar e a seguir incluir os seguintes pacotes nugets no projeto:
<ItemGroup> <PackageReference Include="sqlite-net-pcl" Version="1.8.116" /> <PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.5" /> <PackageReference Include="SQLitePCLRaw.core" Version="2.1.5" /> <PackageReference Include="SQLitePCLRaw.provider.dynamic_cdecl" Version="2.1.5" /> <PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.5" /> </ItemGroup> |
1- sqlite-
2- SQLitePCLRaw.bundle_green
versão
2.1.5
3- SQLitePCL.Raw.core versão
2.1.5
4- SQLitePCLRaw.provider.dynamic_cdecl
versão
2.1.5
5- SQLitePCLRaw.provider.sqlite3
versão 2.1.5
Ao usar o SQLite em uma aplicação .NET MAUI, você precisa incluir este cinco pacotes no seu projeto. Cada pacote desempenha um papel específico no acesso e funcionamento do SQLite no .NET.
Esses pacotes são necessários para permitir o acesso ao SQLite em uma aplicação .NET MAUI, fornecendo as funcionalidades e as abstrações necessárias para trabalhar com o banco de dados SQLite em diferentes plataformas suportadas.
Criando as pastas no projeto
Para organizar o código no nosso projeto vamos criar uma pasta MVVM e dentro desta pasta criar as pastas : Models, Views e ViewModels.
A seguir vamos criar uma pasta Services onde iremos criar o serviço para acessar o SQLite. Abaixo temos a estrutura da solução criada:
Criando o domínio e os serviços
using
SQLite; namespace MeusLivros.MVVM.Models; [Table("Livros")] public class Livro { [PrimaryKey, AutoIncrement] public int Id { get; set; } [MaxLength(200)] public string Titulo { get; set; } [MaxLength(250)] public string Autor { get; set; } [MaxLength(200)] public string ImagemUrl { get; set; } [MaxLength(200)] public string EmprestadoPara { get; set; } public int Paginas { get; set; } public DateTime DataLancamento { get; set; } public bool LeituraConcluida { get; set; } } |
Nesta classe estamos usando os atributos da biblioteca SQLite.NET para realizar o mapeamento da classe para a tabela no SQLite
Os
atributos mais comuns usados no .NET MAUI para mapear uma classe
para tabelas no SQLite são:
- [Table]: Este atributo especifica o
nome da tabela no banco de dados.
- [Column]: Este atributo especifica o
nome da coluna na tabela.
- [PrimaryKey]: Este atributo
especifica que a coluna é a chave primária da tabela.
- [NotNull]: Este atributo especifica
que a coluna não pode ser nula.
- [ForeignKey]: Este atributo
especifica que a coluna é uma chave estrangeira para outra tabela.
- [Unique]: Este atributo especifica
que a coluna deve ter valores únicos.
- [AutoIncrement]: Este atributo
especifica que a coluna deve ter um valor incrementado
automaticamente.
A seguir vamos criar na pasta Services a interface ILivroService e a classe LivroService onde vamos implementar as operações de acesso aos dados usando o SQLite:
1- ILivroService
using
MeusLivros.MVVM.Models; namespace MeusLivros.Services;public interface ILivroService{ Task InitializeAsync(); Task<IEnumerable<Livro>> GetLivrosAsync(); Task<IEnumerable<Livro>> GetLivroTituloAsync(string titulo); Task<int> CriaLivroAsync(Livro livro); Task<int> DeletaLivroAsync(int id); Task<int> AtualizaLivroAsync(Livro livro); } |
2- LivroService
using
MeusLivros.MVVM.Models; using SQLite; namespace MeusLivros.Services;public class LivroService : ILivroService{ private SQLiteAsyncConnection _dbConnection; public async Task InitializeAsync() { await SetUpDb(); } private async Task SetUpDb() { if (_dbConnection == null) { string dbPath = Path.Combine(Environment. GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LivrosDB.db3"); _dbConnection = new SQLiteAsyncConnection(dbPath); await _dbConnection.CreateTableAsync<Livro>(); } } public async Task<int> CriaLivroAsync(Livro livro) { return await _dbConnection.InsertAsync(livro); } public async Task<int> DeletaLivroAsync(int id) { return await _dbConnection.DeleteAsync(id); } public async Task<int> AtualizaLivroAsync(Livro livro) { return await _dbConnection.UpdateAsync(livro); } public async Task<IEnumerable<Livro>> GetLivrosAsync() { var livros = await _dbConnection.Table<Livro>().ToListAsync(); return livros; } public async Task<IEnumerable<Livro>> GetLivroTituloAsync(string titulo) { var livros = await _dbConnection.Table<Livro>() .Where(x => x.Titulo.Contains(titulo)).ToListAsync(); return livros; } } |
Nesse código , o caminho do banco de dados é definido usando Environment.GetFolderPath para obter o diretório especial LocalApplicationData, que é usado para armazenar dados específicos do aplicativo. Em seguida, é definido o nome do arquivo do banco de dados ("LivrosDB.db3") e o caminho completo do banco de dados é obtido usando Path.Combine.
Temos o método InitializeAsync() que será responsável por configurar o banco de dados. O método SetUpDb() foi alterado para ser assíncrono e retornar uma Task, permitindo o uso de await para aguardar a criação da tabela.
Dessa forma, ao utilizar o serviço, você poderá chamar explicitamente o método InitializeAsync() para realizar a inicialização do banco de dados de forma assíncrona.
Criando as ViewModels
Vamos criar na pasta MVVM/ViewModels as view models usadas no projeto, iniciando com a view model LivrosViewModel que encapsula a lógica de apresentação e interações relacionadas aos livros e que implementa os comandos usando o atributo [RelayCommand] que é uma implementação da interface ICommand que expõe o método para a view.
1-LivrosViewModel
using
CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using MeusLivros.MVVM.Models; using MeusLivros.Services; using System.Collections.ObjectModel; namespace MeusLivros.MVVM.ViewModels;public partial class LivrosViewModel : ObservableObject{ private readonly ILivroService _livroService; public ObservableCollection<Livro> MeusLivros { get; set; } = new(); public LivrosViewModel(ILivroService livroService) { _livroService = livroService; } [RelayCommand] { MeusLivros.Clear(); try { await _livroService.InitializeAsync(); var livros = await _livroService.GetLivrosAsync(); if (livros.Any()) { foreach (var livro in livros) { MeusLivros.Add(livro); } } } catch (Exception ex) { await Shell.Current.DisplayAlert("Error", ex.Message, "OK"); } } [RelayCommand] [RelayCommand] { var result = await Shell.Current.DisplayAlert("Deletar", $"Confirma exclusão do livro : \n\n \"{livro.Titulo}\"?", "Sim", "Não"); if (result is true) { try { await _livroService.InitializeAsync(); await _livroService.DeletaLivroAsync(livro.Id); await GetLivros(); } catch (Exception ex) { await Shell.Current.DisplayAlert("Error", ex.Message, "OK"); } } } [RelayCommand] await Shell.Current.GoToAsync("UpdateLivroPage", new Dictionary<string, object> { {"LivroObject", livro } }); } |
O construtor recebe uma instância de ILivroService como parâmetro e o serviço é injetado por meio de injeção de dependência no construtor.
A propriedade MeusLivros é uma propriedade pública que representa uma coleção de objetos Livro. Ela é do tipo ObservableCollection, e assim vai notificar automaticamente a interface do usuário (View) sobre alterações em seus elementos, garantindo que a interface do usuário seja atualizada quando a coleção for modificada.
A seguir temos os seguintes métodos decorados com o atributo [RelayCommand] que implementa a interface ICommand expondo o método para a view:
Método GetLivros:
Método acionado quando você deseja obter a lista de livros.
GetLivros
para atualizar a lista de livros.Livro
selecionado como
parâmetro.2- AddLivroViewModel
Na página Index.razor vamos exibir uma imagem alunos.png que esta contida na pasta wwwroot/images:
using
CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using MeusLivros.MVVM.Models; using MeusLivros.Services; namespace MeusLivros.MVVM.ViewModels;public partial class AddLivroViewModel : ObservableObject{ private readonly ILivroService _livroService; [ObservableProperty] private string _livroTitulo; [ObservableProperty] private string _livroAutor; [ObservableProperty] private string _livroImagemUrl; [ObservableProperty] private string _livroEmprestadoPara; [ObservableProperty] private int _livroPaginas; [ObservableProperty] private bool _livroLeituraConcluida; public AddLivroViewModel(ILivroService livroService) { _livroService = livroService; } [RelayCommand] { try { if (!string.IsNullOrEmpty(LivroTitulo)) { Livro livro = new() { Titulo = LivroTitulo, Autor = LivroAutor, ImagemUrl = LivroImagemUrl, EmprestadoPara = LivroEmprestadoPara, Paginas = LivroPaginas, LeituraConcluida = LivroLeituraConcluida }; await _livroService.InitializeAsync(); await _livroService.CriaLivroAsync(livro); await Shell.Current.GoToAsync(".."); } else { await Shell.Current.DisplayAlert("Error", "Livro sem Título", "OK"); } } catch (Exception ex) { await Shell.Current.DisplayAlert("Error", ex.Message, "OK"); } } } |
A possui vários campos privados decorados com
[ObservableProperty]
que são usados para armazenar
dados relacionados a um novo livro que o usuário deseja adicionar. Essas
propriedades são observáveis, o que significa que qualquer alteração nelas
notificará automaticamente a interface do usuário (View) para atualizar.
A ViewModel também é responsável por controlar a
lógica de adição de um novo livro à lista de livros do aplicativo. Ela interage
com o serviço de livro para realizar as operações de adição e lida com possíveis
erros durante o processo. As propriedades observáveis são usadas para vincular
os dados da interface do usuário aos campos de entrada do novo livro, permitindo
que a interface do usuário seja atualizada conforme o usuário insere
informações. O uso de [RelayCommand]
permite que um comando seja associado a uma ação de botão na interface do
usuário, que, quando acionado, chama o método
AddLivro.
Temos nesta view model a definição do método decorado com o atributo [RelayCommand]:
LivroTitulo
)
não está vazio ou nulo. Se estiver, exibe um alerta de erro.Livro
com os detalhes
fornecidos pelo usuário (título, autor, URL da
imagem, etc.).Livro
criado.Shell.Current.GoToAsync("..")
).3- UpdateLivroViewModel
using
CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using MeusLivros.MVVM.Models; using MeusLivros.Services; namespace MeusLivros.MVVM.ViewModels;[QueryProperty(nameof(Livro), "LivroObject")]public partial class UpdateLivroViewModel : ObservableObject { private readonly ILivroService _livroService; [ObservableProperty] private Livro _livro; public UpdateLivroViewModel(ILivroService livroService) { _livroService = livroService; } [RelayCommand] private async Task UpdateLivro() { if (!string.IsNullOrEmpty(Livro.Titulo)) { await _livroService.InitializeAsync(); await _livroService.AtualizaLivroAsync(Livro); await Shell.Current.GoToAsync(".."); } else { await Shell.Current.DisplayAlert("Error", "Livro sem Título", "OK"); } } } |
Esta ViewModel é responsável por controlar a lógica de atualização dos detalhes de um livro no aplicativo. Ela recebe um parâmetro de consulta (o objeto do livro a ser atualizado) da página anterior e fornece a funcionalidade para atualizar os detalhes desse livro.
As propriedades observáveis são usadas para vincular os dados da interface do
usuário ao livro a ser atualizado, permitindo que a interface do usuário seja
atualizada conforme o usuário faz alterações. O uso de
[RelayCommand]
permite que um comando seja associado a uma
ação de botão na interface do usuário, que, quando acionado, chama o método
UpdateLivro
.
Nesta view model temos a definição do método decorado com o atributo [RelayCommand]:
[RelayCommand]
.
Ele é acionado quando o usuário deseja atualizar um livro.Livro.Titulo
)
não está vazio ou nulo. Se estiver, exibe um alerta de erro.Livro
armazenado no campo _livro
.Shell.Current.GoToAsync("..")
).Com isso podemos iniciar a criação das Views e fazer a vinculação com as respectivas view models usando o BindingContext.
Faremos isso na próxima parte do artigo.
"Se confessarmos os nossos pecados, ele é fiel e
justo para nos perdoar os pecados, e nos purificar de toda a injustiça."
1 João 1:9
Referências: