Library / SDK
praeclarum/sqlite-net avatar
praeclarum/sqlite-net

sqlite-net: the reflection ORM that ships as one C# file

Simple, powerful, cross-platform SQLite client and ORM for .NET

4,457 stars1,421 forksC#MIT

At a glance

What is it?
sqlite-net is a minimal SQLite client and attribute-driven ORM for .NET, .NET Core, Mono and Xamarin. It is easy to adopt and easy to outgrow, and the NuGet package choice matters more than the API.
Who is it for?
Adopt sqlite-net when you want attribute-mapped CRUD over a local SQLite file and you are willing to live with a thin layer: mobile apps, desktop tools and small services written in C#. Do not adopt it when you need a migration framework, relationship tracking, or a query layer that translates LINQ into SQL, because the README documents none of that and the reflection layer only maps what you declare.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 79 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What sqlite-net actually solves for a C# app with a local database

The problem is mundane and recurring: you have a .NET application, you want a local SQLite file, and you do not want to write ADO.NET command objects, parameter arrays and manual row-to-object mapping for every table. sqlite-net is a thin wrapper over SQLite 3 that adds a small reflection-driven ORM on top. You declare plain classes, mark them with attributes such as [PrimaryKey], [AutoIncrement], [Indexed] and [Ignore], and the library creates tables and moves objects in and out.

The README states the design goals directly: easy integration with existing projects, a thin and fast wrapper that should not be the performance bottleneck of your queries, simple methods for CRUD and parameterised queries returning strongly typed results, and working with your data model without forcing you to change your classes. That last point is the real pitch. There is no base class to inherit, no interface to implement, no mapping file. A class you already have becomes a table once you annotate it.

The audience follows from that. It was first designed for Xamarin.iOS and now targets Xamarin platforms, .NET, .NET Core, Mono, UWP and Azure per the README. The related searches for MAUI and Unity suggest the same crowd: people shipping a client-side app who need persistence, not a data platform.

Attributes, reflection and two APIs: how the mapping layer works

There is no query translator and no change tracker. When you call CreateTable<T>, the library reflects over T's public properties, reads the attributes, and issues the corresponding CREATE TABLE statement. Insert, Update and Delete take an object, read its properties by reflection, and bind them as parameters. Table<T>() returns an object that accepts predicates for WHERE and ORDER BY clauses, and Query<T> runs raw SQL and materialises each row into T.

The materialisation rule is more flexible than a strict ORM allows. The README notes that the generic parameter to Query can be one of your table classes or any other class whose public properties match the columns returned by the query, and it shows aliasing columns in SQL to match a differently shaped class. That means read models do not need to be tables, which is a useful escape hatch when a join returns a shape you do not want to persist.

The two API surfaces are the same layer with different blocking behaviour. SQLiteConnection blocks; SQLiteAsyncConnection wraps calls in the Task Parallel Library so async and await work, and the README recommends the asynchronous API for mobile applications to increase responsiveness. The source is split accordingly: SQLite.cs for the synchronous implementation, SQLiteAsync.cs for the asynchronous one. Because reflection drives everything, the cost is per-call rather than compile-time, and the README's own framing acknowledges this by positioning the library as the thin layer rather than the fast one.

Installing sqlite-net-pcl and running a first query

The README says to install sqlite-net-pcl from NuGet, and adds an instruction that is easy to miss: you need to add the NuGet package to both your .NET Standard library project and your platform-dependent app project. If you only add it to the shared library, the native SQLite bits will not be present in the app that actually runs.

bash
dotnet add package sqlite-net-pcl

If you prefer not to depend on NuGet at all, the README points out that the library is contained in one file. You add SQLite.cs to your project, or SQLiteAsync.cs for the asynchronous implementation, and start creating tables.

Define a model and let the library create the table. This is the README's stock example, trimmed to one entity.

csharp
public class Stock
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string Symbol { get; set; }
}

Open a connection, create the table, and insert. The README notes that with an auto-incremented primary key the generated value is available on the object after the insert, so the Console.WriteLine below prints the new Id.

csharp
var databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MyData.db");

var db = new SQLiteConnection(databasePath);
db.CreateTable<Stock>();

var stock = new Stock() { Symbol = "AAPL" };
db.Insert(stock);
Console.WriteLine("{0} == {1}", stock.Symbol, stock.Id);

Query through the typed table, or drop to raw SQL when the predicate API is not enough. Both forms come from the README.

csharp
var query = db.Table<Stock>().Where(v => v.Symbol.StartsWith("A"));
foreach (var stock in query)
    Console.WriteLine("Stock: " + stock.Symbol);

var valuations = db.Query<Valuation>("select * from Valuation where StockId = ?", stock.Id);

The asynchronous variant swaps the connection type and the method names: SQLiteAsyncConnection, CreateTableAsync, InsertAsync, awaited in the usual way. For a first real use, the synchronous API is the shorter path; switch to async when the call sits on a UI thread.

Where sqlite-net stops: no migrations, no relationships, no LINQ to SQL

The README is honest about scope by omission. There is no migration system described. CreateTable creates a table; it does not document altering one when your class gains a property, and the README does not document rollback or schema versioning. If your schema will change after users have data on disk, you are writing that logic yourself, including the ALTER TABLE statements and the version bookkeeping.

Relationships are absent in the same way. An [Indexed] property is an index, not a foreign key, and the README's Valuation example stores a StockId as a plain int. Loading a stock with its valuations is two queries you write, not a navigation property the library resolves. There is no identity map or change tracking either: Update writes the object you hand it.

Table<T>().Where(...) is a predicate builder over the table, not a LINQ provider. The README shows StartsWith in a predicate, but it does not document a general translation of arbitrary C# expressions into SQL. When an expression does not fit, the documented escape hatch is Query with a SQL string and parameters, which is exactly the low-level path you were trying to avoid. That is the trade: the attribute layer saves you boilerplate for simple CRUD, and the moment your queries get interesting you are back in SQL.

sqlite-net-pcl vs the other packages, and vs using SQLite directly

The three NuGet packages are not interchangeable. sqlite-net-pcl is the .NET Standard library and the one the README tells you to install. sqlite-net-static is a special version that uses P/Invokes against the platform-provided sqlite3, which means you depend on whatever SQLite version the operating system ships. sqlite-net-base ships without a SQLitePCLRaw bundle so you can choose your own provider. The Makefile confirms these are separate pack targets built from the same two source files: SQLite-net-std, SQLite-net-base and SQLite-net-static, plus SQLite-net-e. That structure is the clearest statement of intent: one codebase, several packaging strategies, and the choice is yours to make.

The other real alternative is not a different ORM but no ORM. Microsoft.Data.Sqlite or the raw SQLite API give you full control over statements, transactions and connection lifetime, and they do not reflect over your classes on every call. sqlite-net's answer is that you write less code for the common case and keep the [Ignore] attribute to exclude fields you do not want persisted. If your data access is a handful of queries, the reflection layer is overhead you can skip. If it is dozens of small tables with straightforward shapes, the boilerplate it removes is the point.

A related search asks about sqlite-net and SQLCipher. The README does not mention SQLCipher or encryption at all, so database encryption is not something this library documents; if you need it, that is a decision to make at the provider level, not through an attribute.

Release cadence, licence and what an upgrade costs

The repository is not archived and the last push was on 2026-07-13, so the project is still receiving changes. The release history is sparse rather than steady: v1.9.172 in March 2024, which the release notes label .NET 8 support, v1.8.116 in September 2021, and v1.7.335 in May 2020. Reading that as a cadence, you should expect long gaps between tagged releases and plan accordingly. Pin a version rather than floating.

Upgrading is cheap in the normal case, because the surface is two files and a set of attributes. The cost sits in the packaging decision. If you move from sqlite-net-pcl to sqlite-net-base to control the provider, or to sqlite-net-static to use the platform sqlite3, you are changing which native SQLite your app loads, and that is where behaviour differences will appear. The .NET 8 support in v1.9.172 is the kind of change that matters for target framework selection.

The licence is MIT, per the repository. MIT is permissive: it allows use, modification and redistribution with the copyright notice and permission notice retained, and it comes with no warranty. That is a summary of the licence text, not legal advice; read LICENSE.txt in the repository if the terms matter to your organisation. The practical implication is that embedding SQLite.cs in a closed-source product is consistent with the licence, provided you keep the notice.

Editorial conclusion

Adopt sqlite-net when you want attribute-mapped CRUD over a local SQLite file and you are willing to live with a thin layer: mobile apps, desktop tools and small services written in C#. Do not adopt it when you need a migration framework, relationship tracking, or a query layer that translates LINQ into SQL, because the README documents none of that and the reflection layer only maps what you declare. Before committing, verify which NuGet package fits your platform (sqlite-net-pcl bundles SQLitePCLRaw, sqlite-net-static P/Invokes the platform sqlite3, sqlite-net-base lets you choose your own provider), and confirm that the version you install supports your target framework, since v1.9.172 is the release that added .NET 8 support.

Frequently asked questions

What is sqlite-net-pcl?

It is the .NET Standard NuGet package for sqlite-net, a minimal SQLite client and ORM for .NET. The README instructs you to install it into both your .NET Standard library project and your platform-dependent app project.

What is a sqlite-net-pcl alternative?

The README lists two sibling packages rather than competitors: sqlite-net-static, which uses P/Invokes to the platform-provided sqlite3, and sqlite-net-base, which ships without a SQLitePCLRaw bundle so you can choose your own provider. Outside the project, using SQLite directly avoids the reflection layer entirely.

How do I install sqlite-net?

Install the sqlite-net-pcl package from NuGet, and add it to both your .NET Standard library project and your platform-dependent app project. Alternatively, the README notes the library is contained in one file, so you can add SQLite.cs (or SQLiteAsync.cs) directly to your project.

Does sqlite-net support async operations?

Yes. SQLiteAsyncConnection uses the Task Parallel Library, so async and await work normally, with methods such as CreateTableAsync and InsertAsync. The README suggests the asynchronous API for mobile applications to increase responsiveness.

Does sqlite-net handle database migrations?

The README does not document a migration system, schema versioning or rollback. CreateTable creates tables, and altering an existing table after your class changes is logic you write yourself.

Does sqlite-net support encryption with SQLCipher?

The README does not mention SQLCipher or encryption. If you need an encrypted database, that is a provider-level decision, and sqlite-net-base exists so you can choose your own SQLitePCLRaw provider.

Official sources

  1. Issues
  2. License: MIT
  3. praeclarum/sqlite-net on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/praeclarum-sqlite-net.svg)](https://hysenlabs.com/projects/praeclarum-sqlite-net)