askill
maui-sqlite-database

maui-sqlite-databaseSafety 85Repository

Add SQLite local database storage to .NET MAUI apps using sqlite-net-pcl. Covers data models with ORM attributes, async database service with lazy init, DI registration, WAL mode, and file management. Works with any UI pattern (XAML/MVVM, C# Markup, MauiReactor). USE FOR: "SQLite database", "local database", "sqlite-net-pcl", "offline storage", "CRUD database", "database service", "WAL mode", "CreateTableAsync", "local data persistence", "ORM attributes". DO NOT USE FOR: REST API data fetching (use maui-rest-api), secure credential storage (use maui-secure-storage), or file picking (use maui-file-handling).

43 stars
1.2k downloads
Updated 2/19/2026

Package Files

Loading files...
SKILL.md

SQLite Local Database for .NET MAUI

NuGet Package

Install sqlite-net-pcl by praeclarum:

<PackageReference Include="sqlite-net-pcl" Version="1.9.*" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.*" />

[!WARNING] The correct package is sqlite-net-pcl (author: praeclarum). Do NOT use Microsoft.Data.Sqlite, sqlite-net, or SQLitePCL.raw directly — they are different libraries with incompatible APIs.


1. Constants Class

public static class Constants
{
    public const string DatabaseFilename = "app.db3";

    public const SQLite.SQLiteOpenFlags Flags =
        SQLite.SQLiteOpenFlags.ReadWrite |
        SQLite.SQLiteOpenFlags.Create |
        SQLite.SQLiteOpenFlags.SharedCache;

    public static string DatabasePath =>
        Path.Combine(FileSystem.AppDataDirectory, DatabaseFilename);
}
  • ReadWrite | Create | SharedCache — standard flags for mobile apps.
  • Use FileSystem.AppDataDirectory (not Environment.GetFolderPath) for cross-platform correctness on all MAUI targets.

2. Data Model

using SQLite;

public class TodoItem
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }

    [MaxLength(250)]
    public string Name { get; set; } = string.Empty;

    public bool Done { get; set; }
}

Key attributes: [PrimaryKey], [AutoIncrement], [MaxLength(n)], [Indexed], [Ignore], [Column("name")], [Unique], [NotNull], [Table("name")].


3. Database Service

Use the lazy async initialization pattern — the connection is created once, on first access, and all callers await the same instance:

using SQLite;

public class DatabaseService
{
    private SQLiteAsyncConnection? _database;

    private async Task<SQLiteAsyncConnection> GetDatabaseAsync()
    {
        if (_database is not null)
            return _database;

        _database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
        await _database.ExecuteAsync("PRAGMA journal_mode=WAL;");
        await _database.CreateTableAsync<TodoItem>();
        return _database;
    }

    public async Task<List<TodoItem>> GetItemsAsync()
    {
        var db = await GetDatabaseAsync();
        return await db.Table<TodoItem>().ToListAsync();
    }

    public async Task<List<TodoItem>> GetItemsAsync(bool done)
    {
        var db = await GetDatabaseAsync();
        return await db.Table<TodoItem>().Where(i => i.Done == done).ToListAsync();
    }

    public async Task<TodoItem?> GetItemAsync(int id)
    {
        var db = await GetDatabaseAsync();
        return await db.Table<TodoItem>().Where(i => i.Id == id).FirstOrDefaultAsync();
    }

    public async Task<int> SaveItemAsync(TodoItem item)
    {
        var db = await GetDatabaseAsync();
        return item.Id != 0
            ? await db.UpdateAsync(item)
            : await db.InsertAsync(item);
    }

    public async Task<int> DeleteItemAsync(TodoItem item)
    {
        var db = await GetDatabaseAsync();
        return await db.DeleteAsync(item);
    }

    public async Task CloseConnectionAsync()
    {
        if (_database is not null)
        {
            await _database.CloseAsync();
            _database = null;
        }
    }
}

4. DI Registration

Register as a singleton in MauiProgram.cs:

builder.Services.AddSingleton<DatabaseService>();

Singleton because SQLiteAsyncConnection is not thread-safe for multiple instances. A single connection with WAL handles concurrent reads efficiently.

Inject into view models or pages:

public class TodoListViewModel(DatabaseService database)
{
    private readonly DatabaseService _database = database;
}

5. WAL (Write-Ahead Logging)

Enabled in GetDatabaseAsync() via PRAGMA journal_mode=WAL;.

  • Readers do not block writers and vice versa.
  • Better performance for concurrent read/write workloads.
  • Recommended for all MAUI apps.

6. Database File Management

Always close the connection before moving, copying, or deleting the file.

// Delete database
await databaseService.CloseConnectionAsync();
if (File.Exists(Constants.DatabasePath))
    File.Delete(Constants.DatabasePath);

// Export / backup
await databaseService.CloseConnectionAsync();
File.Copy(Constants.DatabasePath,
    Path.Combine(FileSystem.CacheDirectory, "backup.db3"), overwrite: true);

Platform paths for FileSystem.AppDataDirectory:

PlatformLocation
Android/data/user/0/{package}/files
iOSApp sandbox Library (iCloud-backed)
Mac CatalystApp sandbox Library/Application Support
Windows%LOCALAPPDATA%\Packages\{id}\LocalState

On iOS, use FileSystem.CacheDirectory to exclude from iCloud backup.


7. Common Patterns

// Raw SQL
var items = await db.QueryAsync<TodoItem>(
    "SELECT * FROM TodoItem WHERE Done = ?", 1);

// Multiple tables — add in GetDatabaseAsync()
await _database.CreateTableAsync<TodoItem>();
await _database.CreateTableAsync<Category>();

// Transactions
await db.RunInTransactionAsync(conn =>
{
    conn.Insert(item1);
    conn.Insert(item2);
});

// Drop and recreate
await db.DropTableAsync<TodoItem>();
await db.CreateTableAsync<TodoItem>();

Checklist

  • Install sqlite-net-pcl + SQLitePCLRaw.bundle_green
  • Create Constants with DatabasePath using FileSystem.AppDataDirectory
  • Define models with [PrimaryKey, AutoIncrement]
  • Create DatabaseService with lazy async init and CRUD methods
  • Enable WAL via PRAGMA journal_mode=WAL
  • Register DatabaseService as singleton in DI
  • Close connections before moving or deleting the database file

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

93/100Analyzed 2/24/2026

High-quality technical skill for adding SQLite to .NET MAUI apps. Provides comprehensive, well-structured guidance with code examples for data models, async database service, DI registration, WAL mode, and file management. Includes USE FOR/DO NOT USE FOR scope clarification, helpful warnings, platform path table, and implementation checklist. Located in dedicated skills folder with appropriate tags. Score reflects excellent completeness, actionability, clarity, and reusability with only minor gaps in safety documentation.

85
95
90
95
95

Metadata

Licenseunknown
Version-
Updated2/19/2026
Publisherdavidortinau

Tags

databaseobservability