Magic.IndexedDb
2.0.3
dotnet add package Magic.IndexedDb --version 2.0.3
NuGet\Install-Package Magic.IndexedDb -Version 2.0.3
<PackageReference Include="Magic.IndexedDb" Version="2.0.3" />
<PackageVersion Include="Magic.IndexedDb" Version="2.0.3" />
<PackageReference Include="Magic.IndexedDb" />
paket add Magic.IndexedDb --version 2.0.3
#r "nuget: Magic.IndexedDb, 2.0.3"
#:package Magic.IndexedDb@2.0.3
#addin nuget:?package=Magic.IndexedDb&version=2.0.3
#tool nuget:?package=Magic.IndexedDb&version=2.0.3
Magic IndexedDB
Magic IndexedDB is a C#-first LINQ-to-IndexedDB query engine and typed browser database library for Blazor. It lets .NET applications query IndexedDB with C# expression trees while preserving the performance characteristics of a browser-native database.
Instead of treating LINQ as an in-memory filter over an already-loaded collection, Magic IndexedDB translates supported predicates into an IndexedDB-aware query plan. It uses single-field and compound indexes where possible, partitions complex AND/OR expressions, and uses an optimized cursor engine for operations that IndexedDB cannot execute through an index.
Beneath the current C# API is a language-neutral predicate and schema model. The C# wrapper is the first implementation, but the translation boundary is designed so other languages and frameworks can build wrappers that target the same browser query planner instead of recreating its indexing, cursor, and optimization logic.
Documentation · NuGet · .NET 10 upgrade notes · Issues
Why use Magic IndexedDB?
- Write browser database queries in C#. Use strongly typed predicates instead of maintaining a separate JavaScript data-access layer.
- Build on a universal query model. Additional language wrappers can translate their native query intent into the same predicate tree and browser execution engine.
- Keep filtering close to the data. Compatible equality, range, membership, ordering, and compound-key operations are planned around IndexedDB indexes.
- Express real application logic. Nested
&&and||predicates are translated, partitioned, optimized, and de-duplicated by primary key. - Choose the execution strategy deliberately.
Where(...)preserves opportunities for index optimization;Cursor(...)explicitly selects cursor evaluation when a scan is appropriate. - Process large results progressively.
AsAsyncEnumerable()streams interop results so applications can begin processing before materializing the full returned collection. - Define schemas in C#. Tables describe their primary keys, indexes, compound indexes, persisted names, and valid databases through typed contracts.
- Store practical object models. The current release supports nested objects and collections, custom JSON converters, Unicode and escaped text, and explicit constructor materialization.
Magic IndexedDB is a strong fit for offline-first Blazor applications, progressive web apps, local browser caches, disconnected workflows, and client-side datasets that need more than simple key/value access.
C# first, universal by design
Magic IndexedDB deliberately separates the language-facing wrapper from the engine that plans and executes browser queries:
- A language wrapper translates native query expressions and schema definitions.
- The universal layer represents predicates, logical groups, operations, query additions, and persisted schema names in a language-neutral form.
- The browser engine partitions and optimizes that intent across primary keys, indexes, compound indexes, and cursor execution.
Today, the supported public wrapper is the C# and Blazor API. A future TypeScript, JavaScript, Python, or other language wrapper could produce the same universal intent and reuse the same IndexedDB engine rather than starting over. Building a wrapper still requires semantic translation, schema mapping, validation, and transport compatibility; the internal JavaScript protocol is not yet presented as an independently versioned public SDK.
See the universal predicate language and query engine architecture for the wrapper contract and execution model.
How it works
- The Blazor wrapper reads a supported C# expression tree.
- Magic converts it into a language-neutral predicate tree.
- The browser planner checks available primary keys, indexes, and compound indexes.
- Compatible branches run as native IndexedDB queries through Dexie.js.
- Remaining branches use the cursor engine, with metadata-first selection when pagination or first/last selection requires it.
- Results return as a materialized list or a progressive async stream.
This provides a LINQ-oriented programming model without pretending IndexedDB is SQL or in-memory LINQ. The differences are documented so query behavior remains explicit and predictable.
Requirements
The current codebase targets .NET 10.
The current package supports Blazor WebAssembly and Blazor applications using JavaScript interop over SignalR. Browser storage behavior and quota remain controlled by the user's browser.
Quick start
Install the package:
dotnet add package Magic.IndexedDb
Register it in a standalone Blazor WebAssembly application:
using Magic.IndexedDb;
builder.Services.AddMagicBlazorDB(
BlazorInteropMode.WASM,
builder.HostEnvironment.IsDevelopment());
Add the namespace and inject the scoped service into a Razor component:
@using Magic.IndexedDb
@inject IMagicIndexedDb MagicDb
Open a typed table query, write data, and execute a predicate:
IMagicQuery<Person> people = await MagicDb.Query<Person>();
await people.AddAsync(new Person
{
Name = "Ada Lovelace",
Age = 36,
IsActive = true
});
List<Person> results = await people
.Where(person => person.Age >= 18 && person.IsActive)
.ToListAsync();
Complex predicates use normal C# expression syntax:
List<Person> matches = await people.Where(person =>
(person.Age >= 18 && person.Age <= 30) &&
(
person.City == "New York" ||
person.City == "San Francisco" ||
person.Name.StartsWith("Ada")
)).ToListAsync();
Process a result progressively when retaining the full returned list is unnecessary:
await foreach (Person person in people
.Where(person => person.IsActive)
.AsAsyncEnumerable(cancellationToken))
{
await ProcessAsync(person, cancellationToken);
}
Continue with installation and configuration, schema setup, and the first complete workflow.
Query behavior worth knowing
- Start with
Where(...); it allows the engine to choose indexed, compound-indexed, and cursor branches. - Use
Cursor(...)when you intentionally want cursor execution, such as scan-oriented text matching or stable cursor pagination. - Magic's pagination chain is
Take(count).Skip(offset)because of how its IndexedDB execution path composes limit and offset operations. ToListAsync()applies the requested materialized ordering.AsAsyncEnumerable()prioritizes progressive delivery and does not promise final arrival order across query branches.CountAsync()on the root query counts the whole table; it is not currently a filtered-count operator.
The Where versus Cursor and ordering and pagination guides explain these contracts in detail.
Documentation
The maintained documentation lives entirely in docs/:
- Installation
- Schema setup
- First application workflow
- Querying guide
WhereversusCursor- Ordering and pagination
- Streaming results
- Database management
- Schema evolution
- Public API reference
- Query expression reference
- Query engine architecture
Version 1 documentation remains available in the legacy archive.
Schema evolution
The automated migration protocol is still under construction. Magic IndexedDB does not automatically migrate existing browser data when a C# model changes. Plan and test persisted-name, index, primary-key, and required-property changes against data produced by the previously released application.
See schema evolution and migrations before changing a deployed schema.
Contributing
Issues and pull requests are welcome. Changes to expression translation, serialization, schema handling, or the JavaScript query engine should include focused unit tests and browser end-to-end coverage where applicable.
See testing and continuous integration for local commands, coverage expectations, and required CI checks.
🏆 Contributors Hall of Fame 🏆
Thank you to all contributors, whether large or small! This section is for the people who have put significant work, care, and energy into the project.
@yueyinqiu — I built this project in about two weeks in 2023, told nobody about it, then walked away and forgot about it. It was not until 2024 that I realized there were pull requests and tickets from other people. Yue provided significant contributions during that time and worked closely with me as we completed version 1 together. This project might have died without you, my friend, and you made it fun for me to come back and see it through. Together we finished version 1 and laid the foundation for version 2.
@Ard2025 — Dude, you came out of left field in 2025 and became a powerhouse contributor! I swear you were a pest control exterminator in a past life because you just cannot stop killing bugs. You have also worked closely with me through valuable brainstorming sessions, major cleanup, refactoring, and much more since the version 2 alpha launch. Seriously, thank you—this project thrives because you are here.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.AspNetCore.Components.Web (>= 10.0.11)
- System.Linq.Async (>= 7.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2.0.3 | 40 | 8/21/2026 | |
| 2.0.2 | 5,679 | 8/22/2025 | |
| 2.0.1 | 5,283 | 6/9/2025 | |
| 2.0.0-alpha9 | 404 | 6/3/2025 | |
| 2.0.0-alpha8 | 306 | 6/2/2025 | |
| 2.0.0-alpha7 | 322 | 5/29/2025 | |
| 2.0.0-alpha6 | 404 | 4/4/2025 | |
| 2.0.0-alpha5 | 200 | 4/4/2025 | |
| 2.0.0-alpha4 | 231 | 4/4/2025 | |
| 2.0.0-alpha3 | 274 | 4/3/2025 | |
| 2.0.0-alpha2 | 266 | 3/31/2025 | |
| 2.0.0-alpha10 | 490 | 6/9/2025 | |
| 2.0.0-alpha1 | 236 | 3/28/2025 | |
| 1.0.12 | 430 | 3/10/2025 | |
| 1.0.11 | 359 | 3/10/2025 | |
| 1.0.10 | 349 | 3/9/2025 | |
| 1.0.6 | 370 | 2/10/2025 | |
| 1.0.4 | 4,578 | 5/11/2023 | |
| 1.0.3 | 621 | 5/10/2023 | |
| 1.0.2 | 580 | 5/3/2023 |