Repository navigation
Getting Started
Working CRUD against a real SQLite database, in about ten minutes. Every snippet below was compiled and run before being written down.
Birko is consumed as source, not NuGet. Clone it next to your own project:
C:\Source\Birko\
├── Framework\ ← git clone https://github.com/Birko-Framework/Birko.Framework.git
└── Consumers\
└── MyApp\ ← your project
That layout matters — the default path resolution below assumes your project sits two levels under a
folder that also contains Framework. Any layout works if you set BIRKO_SRC (see step 2).
Create Directory.Build.props at the root of your repo:
<Project>
<PropertyGroup>
<!-- 1. /p:BirkoSrc=... on the CLI 2. BIRKO_SRC env var 3. the default below -->
<BirkoSrc Condition="'$(BirkoSrc)' == '' and '$(BIRKO_SRC)' != ''">$(BIRKO_SRC)</BirkoSrc>
<BirkoSrc Condition="'$(BirkoSrc)' == ''">$(MSBuildThisFileDirectory)..\..\Framework</BirkoSrc>
</PropertyGroup>
</Project>Three ways to point at the framework, in priority order, so CI and Docker can override it without editing files.
⚠️ This is the step people get wrong. Do not build the list up one project at a time. The graph is dense — importing SQLite stores also needs models, view-models, patterns, rules and helpers — and you discover that one compiler error at a time. (Measured: ten rounds of adding a project and rebuilding, still not closed.)Start from a list that is known to compile and delete what you do not use:
- Birko.Sandbox's app csproj — 38 imports, covering stores, SQL, migrations, caching, workflow, jobs, health, REST, GraphQL and security. This is the better starting point for most applications.
- Birko.Sandbox's compile gate — 163 imports, i.e. nearly everything. Use it if you would rather delete than add.
Make one project that imports Birko, and have your other projects reference it:
MyApp/
├── Directory.Build.props ← step 2
├── MyApp.Birko/ ← the aggregator: imports Birko, nothing else
│ └── MyApp.Birko.csproj
└── MyApp.Api/ ← your code: <ProjectReference> to MyApp.Birko
The aggregator looks like this — abbreviated; take the real list from the link above:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<Import Project="$(BirkoSrc)\Birko.Contracts\Birko.Contracts.projitems" Label="Shared" />
<Import Project="$(BirkoSrc)\Birko.Configuration\Birko.Configuration.projitems" Label="Shared" />
<Import Project="$(BirkoSrc)\Birko.Data.Core\Birko.Data.Core.projitems" Label="Shared" />
<Import Project="$(BirkoSrc)\Birko.Data.Stores\Birko.Data.Stores.projitems" Label="Shared" />
<Import Project="$(BirkoSrc)\Birko.Data.SQL\Birko.Data.SQL.projitems" Label="Shared" />
<Import Project="$(BirkoSrc)\Birko.Data.SQL.SqLite\Birko.Data.SQL.SqLite.projitems" Label="Shared" />
<!-- … and the rest -->
</Project>Why one aggregator? If several projects import overlapping .projitems directly, the same types
get compiled into multiple assemblies and you get duplicate-type errors. One aggregator, referenced
normally, avoids that.
💡 You do not declare Birko's NuGet packages. Each .projitems brings its own
(Microsoft.Data.Sqlite and so on). Declaring them yourself produces duplicate-PackageReference
warnings.
using Birko.Data.Models;
using Birko.Data.SQL.Attributes;
[Table("Products")]
public class Product : AbstractDatabaseModel
{
[MaxLengthField(200)]
public string Name { get; set; } = string.Empty;
[PrecisionField(18), ScaleField(2)]
public decimal Price { get; set; }
}AbstractDatabaseModel gives you a Guid primary key. The table is created automatically on first
use — no migration needed to get started.
Attributes that exist (there is no DecimalField — precision and scale are separate):
| Attribute | Does |
|---|---|
[Table("name")] |
table name |
[MaxLengthField(n)] |
VARCHAR(n) instead of unbounded text |
[PrecisionField(p)], [ScaleField(s)]
|
DECIMAL(p,s) — use both, or you get whole numbers |
[RequiredField] |
NOT NULL |
[UniqueField], [PrimaryField]
|
constraints |
[IndexedField("ix_name")], [CompositeIndex(...)]
|
indexes — both take a name |
[UtcField] |
store a DateTime as a true instant, read back as UTC |
[NamedField("col")] |
different column name |
[IgnoreField] |
don't persist this property |
using Birko.Data.SQL.SqLite.Stores;
var store = new SQLiteStore<Product>();
store.SetSettings(new SqLiteSettings(@"C:\data", "shop.db"));
// create
var id = store.Create(new Product { Name = "Widget", Price = 9.99m });
// read one
var product = store.Read(id);
// query
var cheap = store.Read(x => x.Price < 10m, null, null, null); // filter, orderBy, limit, offset
// update
product!.Price = 12.50m;
store.Update(product);
// count and delete
var total = store.Count();
store.Delete(product);That is the whole loop. Output from running exactly this:
created: True
read: Widget
filtered: Widget
count: 2
💡 SqLiteSettings lives in Birko.Data.SQL.SqLite.Stores, not Birko.Configuration — the
settings class ships with its provider.
The point of the store API is that this swaps cleanly. Same model, same calls:
using Birko.Data.InMemory.Stores;
var store = new InMemoryStore<Product>(); // no settings needed — ideal for testsInMemoryStore is the simplest backend and the recommended test double. See Data and Stores for
the full list and what each is good for.
| Symptom | Cause |
|---|---|
CS0246: type X could not be found for framework types |
Missing .projitems import. Take the full list from the Sandbox aggregator rather than adding one at a time. |
| Duplicate type errors | Two of your projects import the same .projitems. Use a single aggregator. |
NU1504 duplicate PackageReference
|
You declared a package the .projitems already brings. Remove yours. |
| Decimals stored as whole numbers |
[PrecisionField] without [ScaleField]. Both are required. |
| Build works locally, fails in CI |
BirkoSrc resolving elsewhere. Set BIRKO_SRC explicitly in CI. |
- Core Concepts — models, settings, stores and repositories, and how they fit
- Data and Stores — every backend and how to choose
- SQL Databases — providers, indexes, migrations
- Birko.Sandbox — a runnable harness touching each layer
Some documents in the repository are out of date and contradict this page. Where they differ:
-
Birko.Data.SQL.SqLite/CLAUDE.mdlists three dependencies; it actually needs at least eight. -
README.mdsuggests copying "the shared projects you need" — in practice that does not converge. -
PREZENTACIA.mdproject counts predate a large reorganisation.
Start here
Data
Services
UI