All skills
davidortinau avatar

/maui-sqlite-database

@74acbe3
by David Ortinaudavidortinau/maui-skills173 stars
20

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).

Use this Skill: https://skilld.dev/gh/davidortinau/maui-skills/maui-sqlite-database

This session only. Nothing lands on disk.

SKILL.md

β‰ˆ159 tokens always: the name and description. β‰ˆ1.1k when used: this file. β‰ˆ1.2k more on demand in 1 file.

SQLite Database β€” Gotchas & Best Practices

For full service implementation, constants, data model templates, and common patterns, see references/sqlite-database-api.md.

⚠️ Wrong Package Trap

<!-- ❌ WRONG β€” these are different libraries with incompatible APIs -->
<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="sqlite-net" />
<PackageReference Include="SQLitePCL.raw" />

<!-- βœ… CORRECT β€” sqlite-net-pcl by praeclarum + its bundle -->
<PackageReference Include="sqlite-net-pcl" Version="1.9.*" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.*" />

Common Mistakes

❌ Using Environment.GetFolderPath for Database Path

// ❌ Not cross-platform safe β€” fails on some MAUI targets
var path = Path.Combine(Environment.GetFolderPath(
    Environment.SpecialFolder.LocalApplicationData), "app.db3");

// βœ… Use FileSystem.AppDataDirectory for all MAUI platforms
var path = Path.Combine(FileSystem.AppDataDirectory, "app.db3");

❌ Multiple SQLiteAsyncConnection Instances

SQLiteAsyncConnection is not thread-safe for multiple instances pointing at the same file. Use a single instance via DI singleton:

// ❌ Creating new connections per request
public async Task<List<Item>> GetItems()
{
    var db = new SQLiteAsyncConnection(Constants.DatabasePath);
    return await db.Table<Item>().ToListAsync();
}

// βœ… Lazy singleton β€” one connection, created once
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;
}

❌ Forgetting WAL Mode

Without WAL, readers block writers. Always enable it at initialization:

await _database.ExecuteAsync("PRAGMA journal_mode=WAL;");

❌ File Operations on Open Database

// ❌ Moving/deleting while connection is open β€” data corruption
File.Delete(Constants.DatabasePath);

// βœ… Always close first
await databaseService.CloseConnectionAsync();
if (File.Exists(Constants.DatabasePath))
    File.Delete(Constants.DatabasePath);

Platform Pitfalls

Platform Pitfall
iOS FileSystem.AppDataDirectory is iCloud-backed β€” use FileSystem.CacheDirectory to exclude DB from iCloud backup
All Multiple SQLiteAsyncConnection instances to same file β†’ data corruption
All No WAL β†’ readers block writers, poor concurrent performance
All File operations on open DB β†’ corruption

Decision Framework

Question Recommendation
DI lifetime? Singleton β€” one connection, WAL handles concurrent reads
WAL mode? Always enable β€” no reason not to on mobile
Database path? FileSystem.AppDataDirectory β€” never Environment.GetFolderPath
Save pattern? Check Id != 0 β†’ Update, else Insert
Multiple tables? Add all CreateTableAsync<T>() calls in lazy init
Need to export/backup? Close connection first, then File.Copy

Performance Tips

  1. Use transactions for batch writes β€” individual inserts are slow; wrap in RunInTransactionAsync
  2. Add [Indexed] to frequently queried columns β€” especially foreign keys
  3. WAL mode eliminates reader/writer contention
  4. Avoid ToListAsync() on large tables β€” use Where() filtering and pagination
  5. Use raw SQL for complex queries β€” QueryAsync<T> is faster than chained LINQ for joins

Checklist

  • Install sqlite-net-pcl + SQLitePCLRaw.bundle_green (not Microsoft.Data.Sqlite)
  • Database path uses FileSystem.AppDataDirectory
  • Models have [PrimaryKey, AutoIncrement]
  • Single DatabaseService with lazy async init pattern
  • WAL enabled via PRAGMA journal_mode=WAL
  • DatabaseService registered as singleton in DI
  • Connection closed before any file move/copy/delete
  • iOS: DB excluded from iCloud backup if needed

Source: SKILL.md on GitHub

No alerts15d4 checks Β· Risk SAFE
  • Gen Agent Trust Hub15d

    The skill provides legitimate best practices and code templates for integrating SQLite databases into .NET MAUI applications. It correctly guides developers through package selection, thread-safe connection management, and platform-specific file handling without any malicious behavior.

  • Socket15d

    No alerts

  • Snyk15d

    Risk: LOW Β· No issues

  • Runlayer7mo

    1 file scanned Β· No issues

Signed by skilld at 74acbe3. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 6 months ago

README badge

README badge for davidortinau/maui-skills/maui-sqlite-database