sqlite-net: one C# file, two APIs, and four package builds
Simple, powerful, cross-platform SQLite client and ORM for .NET
At a glance
- What is it?
- sqlite-net wraps SQLite 3 for .NET in a single source file with a small reflection-based ORM, published as four NuGet packages from one Makefile. Its documentation is thinner than its surface area, so the interesting decisions are which package you reference, which API you call, and what the library leaves you to write yourself.
- Who is it for?
- sqlite-net fits a .NET app that wants a local database file, a plain POCO model and SQL it writes by hand, and it fits best when you copy SQLite.cs into the project and own it. A team that needs schema migrations or an encrypted database should check the Wiki first, since the README's examples do not cover them.
- 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 124 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 November 14, 2026, and from our analysis. They are not legal advice.
Star history
- praeclarum/sqlite-net4,454 ★ · -3
| Repository | 2026-09-23 | 2026-11-14 |
|---|---|---|
| praeclarum/sqlite-net | 4.5k | 4.5k |
-3 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
One source file, and a Makefile that packs four packages from it
The library is two files. The Makefile names them on the first line of its variable block, and every packaging target depends on exactly those two:
SRC=src/SQLite.cs src/SQLiteAsync.csFrom that pair it builds four packages, one `dotnet pack` per output:
pclnuget: nuget/SQLite-net-std/SQLite-net-std.csproj $(SRC)
dotnet pack -c Release -o $(PACKAGES_OUT) $<The four targets are enuget, pclnuget, basenuget and staticnuget. The README's package table offers three of them to users: sqlite-net-pcl, sqlite-net-static and sqlite-net-base. SQLite-net-e is built by the Makefile and absent from that table, and the pcl target's project file sits in a directory called SQLite-net-std rather than SQLite-net-pcl. Small naming gaps, but they are the kind you meet when you go looking for the project file you expected the package name to point at.
The pcl package has to be referenced from two projects
The install instruction is one sentence with a bold warning attached: add sqlite-net-pcl from NuGet to both your .NET Standard library project and your platform-dependent app project. One reference in the shared library does not cover the app.
The second route avoids that entirely. SQLite-net is all contained in one file, so you can add SQLite.cs straight to the project, and SQLiteAsync.cs if you want the asynchronous side. The price is ownership: the file is now yours to update, and none of the NuGet package fixes arrive on their own.
First use is three lines. Point the connection at a file path, construct SQLiteConnection, and create tables from your classes:
// Get an absolute path to the database file
var databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MyData.db");
var db = new SQLiteConnection(databasePath);
db.CreateTable<Stock>();
db.CreateTable<Valuation>();CreateTable reads the model and emits the schema. Nothing else has to be written down.
PrimaryKey, AutoIncrement, Indexed and Ignore are the whole schema layer
The model carries the schema in attributes. A plain class is a table, and the library's stated goal is to work with your data model without forcing you to change your classes, using a small reflection-driven ORM layer:
public class Stock
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Symbol { get; set; }
}The second class shows the other two attributes in use:
public class Valuation
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[Indexed]
public int StockId { get; set; }
public DateTime Time { get; set; }
public decimal Price { get; set; }
[Ignore]
public string IgnoreField { get; set; }
}`[Ignore]` keeps a property out of the table, which is how a computed or navigation property stays in your class without becoming a column. `[Indexed]` asks for an index on a foreign key. In the README example, that is the schema description: four attributes, one call, and no schema object to keep in sync with your classes.
Insert writes the auto-increment key back into your object
The pattern below constructs the object, passes it in, and then reads the generated key off the same instance:
public static void AddStock(SQLiteConnection db, string symbol) {
var stock = new Stock() {
Symbol = symbol
};
db.Insert(stock);
Console.WriteLine("{0} == {1}", stock.Symbol, stock.Id);
}`Update` and `Delete` follow the same shape. For reads, `Table` takes predicates for WHERE clauses and ORDER BY clauses and returns something you enumerate:
var query = db.Table<Stock>().Where(v => v.Symbol.StartsWith("A"));
foreach (var stock in query)
Console.WriteLine("Stock: " + stock.Symbol);When the shape of the result does not match a table class, `Query` takes the SQL and a type to build, and column aliases do the mapping:
public class Val
{
public decimal Money { get; set; }
public DateTime Date { get; set; }
}
public static IEnumerable<Val> QueryVals (SQLiteConnection db, Stock stock) {
return db.Query<Val> ("select \"Price\" as \"Money\", \"Time\" as \"Date\" from Valuation where StockId = ?", stock.Id);
}Low-level statements go through `Execute`. Note the parameter: the sample binds `stock.Id` as a `?` placeholder rather than concatenating it.
The synchronous connection blocks one call at a time, the async one does not
Once the model exists you pick an API, and the difference is blocking. The synchronous API serialises calls, the asynchronous one does not, and mobile developers are pointed at the async side for responsiveness. Same table creation, different connection type:
// Get an absolute path to the database file
var databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MyData.db");
var db = new SQLiteAsyncConnection(databasePath);
await db.CreateTableAsync<Stock>();
Console.WriteLine("Table created!");Writes become `InsertAsync`, `UpdateAsync` and `DeleteAsync`, and the key still lands on the object:
var stock = new Stock()
{
Symbol = "AAPL"
};
await db.InsertAsync(stock);
Console.WriteLine("Auto stock id: {0}", stock.Id);Reads return an `AsyncTableQuery` you add predicates to, and the implementation sits on the Task Parallel Library, so `Task`, `async` and `await` behave as they do elsewhere. Worth noting when you copy files in by hand: SQLiteAsync.cs is a second file, and a project that takes only SQLite.cs has no async API at all.
No migrations, no provider beyond Table, and no encryption story
The gaps matter more than the feature set here, because the surface is small enough that each gap is felt.
The README's examples show no migration mechanism, change tracking or unit of work, so check the Wiki before assuming any of them exist.
`Query` widens the mapping: any class whose public properties match the returned columns becomes a row type.
The Wiki is named as the complete documentation. Whatever lives there is the part the README does not carry.
The newest tag is from March 2024 and master has moved since
Release history is thin and dated. v1.7.335 arrived on 2020-05-16 with .NET Standard 2.0 and async fixes, v1.8.116 on 2021-09-02, and v1.9.172 on 2024-03-27, described as v1.9 with .NET 8 support. After that the tags stop, while the default branch is master and the last push to it was on 2026-07-13.
That gap is the upgrade decision. Pin v1.9.172 and you run code from March 2024 with whatever .NET 8 support it carried, and you miss more than two years of commits. Stay on master and you run unreleased code with no version string to record. The README describes the project as welcoming contributions, and the tree backs that up with a tests directory plus a coverage target that runs the suite under AltCover and feeds the result to reportgenerator. Tests and a licence file are present; a tagged build for the current state of master is not.
The licence is MIT, in LICENSE.txt at the top level, and the coverage report is published alongside the badges at the top of the README.
Editorial conclusion
sqlite-net fits a .NET app that wants a local database file, a plain POCO model and SQL it writes by hand, and it fits best when you copy SQLite.cs into the project and own it. A team that needs schema migrations or an encrypted database should check the Wiki first, since the README's examples do not cover them. Before pinning a version, check whether the March 2024 tag v1.9.172 carries what you need or whether you are willing to run master, which was pushed on 2026-07-13.
Frequently asked questions
what is sqlite net pcl
sqlite-net-pcl is the .NET Standard build of sqlite-net, a small library that stores data in SQLite 3 databases from .NET, .NET Core and Mono applications. The install note says to add the package to both your .NET Standard library project and your platform-dependent app project.
sqlite net pcl alternative
The same repository ships two other packages: sqlite-net-static, which uses P/Invokes to a platform-provided sqlite3, and sqlite-net-base, which comes without a SQLitePCLRaw bundle so you can choose your own provider. The README names no alternative library.
Does sqlite-net change the classes in my model?
No. The design goal is that it works with your data model without forcing you to change your classes, through a small reflection-driven ORM layer. Attributes such as PrimaryKey, AutoIncrement, Indexed and Ignore control what becomes a table.
How do I map a database column to a differently named property in sqlite-net?
Alias the column in the SQL and let Query build your type. The example rewrites a valuation query as select "Price" as "Money", "Time" as "Date" so the result maps to a class with Money and Date properties.
When should I use the asynchronous API in sqlite-net?
The synchronous API blocks one call at a time and the asynchronous API does not, and the README points mobile applications at the async side for responsiveness. It lives in a second source file, SQLiteAsync.cs, using the Task Parallel Library.
Official sources
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.
[](https://hysenlabs.com/projects/praeclarum-sqlite-net)Star history chart for your README
[](https://hysenlabs.com/projects/praeclarum-sqlite-net)Was this useful?