BlazorEssentials.IndexedDb
3.0.0-CI-20241025-225746
dotnet add package BlazorEssentials.IndexedDb --version 3.0.0-CI-20241025-225746
NuGet\Install-Package BlazorEssentials.IndexedDb -Version 3.0.0-CI-20241025-225746
<PackageReference Include="BlazorEssentials.IndexedDb" Version="3.0.0-CI-20241025-225746" />
paket add BlazorEssentials.IndexedDb --version 3.0.0-CI-20241025-225746
#r "nuget: BlazorEssentials.IndexedDb, 3.0.0-CI-20241025-225746"
// Install BlazorEssentials.IndexedDb as a Cake Addin #addin nuget:?package=BlazorEssentials.IndexedDb&version=3.0.0-CI-20241025-225746&prerelease // Install BlazorEssentials.IndexedDb as a Cake Tool #tool nuget:?package=BlazorEssentials.IndexedDb&version=3.0.0-CI-20241025-225746&prerelease
BlazorEssentials.IndexedDb
This is a Blazor library for accessing IndexedDB, it uses Jake Archibald's idb library for handling access to IndexedDB API.
It maps as closely to the browser IndexedDB API as possible, but in a .NET way, so you can use public documentation.
Features
- A clean, simple, intuitive async-first .NET API for IndexedDB
- Create in-browser databases that feel more like the Entity Framework DbContext
- Support for multiple databases in the same application
- Uses Blazor JavaScript Isolation, no script installation required
- Supports .NET 8.0 using idb 8.0.0
Code Provenance
This library is a fork of BlazorIndexedDbJs, which in turn is a fork of Blazor.IndexedDB.
The original library was licensed under the MIT License, and this library is as well.
Differences from BlazorIndexedDbJs
- Refactored to be .NET-first
- Objects prefixed with
IndexedDb
instead ofIDB
(the later conflicts with C# interface naming conventions) - Async method names now end in
Async
- Objects prefixed with
- More automagic
- Constructor-based, attribute-based, or reflection-based initialization (see examples below)
- The default codepath sets the name of your Database and ObjectStores based on the class or property name
- Redesigned JSInterop
- Uses JS Isolation (Blazor dynamically loads the JS as modules)
- Multiple database instance support
- Better tracking for database open state for fewer runtime errors
- Re-engineered build process
- Uses MSBuild-based TypeScript compilation
- Eliminates WebPack
- Uses SkyPack to load third-party modules like
idb
remotely, resulting in smaller package sizes
Demo
You can see a demo of using IndexedDbDatabase
and ViewModels together in our Sample App.
Using the library
requires
NET 8.0 or newer
Step 1: Install NuGet package
Install-Package BlazorEssentials.IndexedDb
or
dotnet add package BlazorEssentials.IndexedDb
Step 2: Make the necessary classes available in your Razor files
Add the following to your _Imports.razor file:
@using CloudNimble.BlazorEssentials.IndexedDb
Step 3: Create an IndexedDBDatabase
class
This file should feel very similar to a DbContext class. Here is a basic implementation, using one of my favorite childhood restaurants as an example:
Data/TheSpaghettiFactoryDb.cs
using Microsoft.JSInterop;
using CloudNimble.BlazorEssentials.IndexedDb;
namespace BlazorEssentials.IndexedDb.Demo.Data
{
public class TheFactoryDb: IndexedDbDatabase
{
public IndexedDbObjectStore Employees { get; }
public TheSpaghettiFactoryDb(IJSRuntime jsRuntime): base(jsRuntime)
{
Name = "TheSpaghettiFactory";
Version = 1;
}
}
}
Or you can customize it with attributes. In the below example:
- the database name will be "TheSpaghettiFactoryDb"
- the table name will be "FiredEmployees"
- the ID for the table is the "id" property
- you wll be expected to manage your own keys
- there will be an index on the
firstName
property for the FiredEmployeesIndexedDbObjectStore
(table)
Data/TheSpaghettiFactoryDb.cs
using Microsoft.JSInterop;
using CloudNimble.BlazorEssentials.IndexedDb;
namespace BlazorEssentials.IndexedDb.Demo.Data
{
public class TheSpaghettiFactoryDb: IndexedDbDatabase
{
[ObjectStore(Name = "FiredEmployees", AutoIncrementKeys = false)]
[Index(Name = "FirstName", KeyPath = "firstName")]]
public IndexedDbObjectStore Employees { get; }
public TheSpaghettiFactoryDb(IJSRuntime jsRuntime): base(jsRuntime)
{
Version = 1;
}
}
}
Step 4. Add each database to your Blazor application's Dependency Injection container
For Blazor WebAssembly, in program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
// RWM: You can add this as a Singleton because a WebAssembly app runs in the browser and only has one "user".
builder.Services.AddSingleton<TheSpaghettiFactoryDb>();
await builder.Build().RunAsync();
}
}
For Blazor Web, in startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
// RWM: Here the database is scoped because each user has their own session.
services.AddScoped<TheSpaghettiFactoryDb>();
}
Step 5: Use the database in your Blazor components
For the following examples we are going to assume that we have Person class which is defined as follows:
public class Person
{
public long? Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Mote that you DO NOT have to decorate your objects with any attributes. As IndexedDb is a NoSQL database, it is schema-less, so your object will be serialized and deserialized using the default JSON serializer.
You can also mix types in the same ObjectStore (table), but be careful is you use numbers for keys or objects may collide.
Accessing the IndexedDbDatabase
To use IndexedDB in a component or page, first inject the IndexedDbDatabase
instance, in this case the TheSpaghettifactoryDb
class.
@inject TheSpaghettiFactoryDb database
Open database
This will create the database if it not exists and will upgrade schema to new version if it is older.
NOTE: Query calls will do this automatically if the database is not already open.
await database.Open()
Getting all records from a store
var people = await database.Employees.GetAllAsync<Person>();
Get one record by Id
var person = await database.Employees.GetAsync<long, Person>(id);
Getting one record using an index
var person = await database.Employees.FirstName.GetAsync<string, Person>("John");
Getting all records from an index
var people = await database.Employees.FirstName.GetAllAsync<string, Person>("John");
Adding a record to an IDBObjectStore
var newPerson = new Person() {
FirstName = "John",
LastName = "Doe"
};
await database.Employees.AddAsync(newPerson);
Updating a record
await database.Employees.PutAsync<Person>(recordToUpdate)
Deleting a record
await database.Employees.DeleteAsync<int>(id)
Clear all records from a store
await database.Employees.ClearAsync()
Deleting the database
await database.DeleteDatabaseAsync()
API
IndexedDbDatabase
Properties
name
public string Name
version
public int Version
objectStores
public IList<IndexedDBObjectStore> ObjectStores
Constructor
public IndexedDBDatabase(IJSRuntime jsRuntime)
Methods
open()
public async Task OpenAsync();
deleteDatabase()
public async Task DeleteDatabaseAsync();
IndexedDbObjectStore
Properties
name
public string Name
keyPath
public string? KeyPath
autoIncrement
public bool AutoIncrement
Indexes
public IList<IndexedDbIndex> Indexes
IndexedDbDatabase
public IndexedDbDatabase IndexedDbDatabase
Constructors
public IndexedDbObjectStore(IndexedDbDatabase database, ObjectStoreAttribute attribute = null);
public IndexedDbObjectStore(IndexedDbDatabase database, string name, string keyPath = "id", bool autoIncrement = false)
Methods
add()
public async Task AddAsync<TData>(TData data);
public async Task AddAsync<TData, TKey>(TData data, TKey key);
put()
public async Task PutAsync<TData>(TData data);
public async Task PutAsync<TData, TKey>(TData data, TKey key);
delete()
public async Task DeleteAsync<TKey>(TKey key);
clear()
public async Task ClearStoreAsync();
Batch (add/put/delete) functions
public async Task BatchAddAsync<TData>(TData[] data);
public async Task BatchPutAsync<TData>(TData[] data);
public async Task BatchDeleteAsync<TKey>(TKey[] key);
count()
public async Task<int> CountAsync();
public async Task<int> CountAsync<TKey>(TKey key);
public async Task<int> CountAsync<TKey>(IDBKeyRange<TKey> key);
get()
public async Task<TResult?> GetAsync<TKey, TResult>(TKey key);
getAll()
public async Task<List<TResult>> GetAllAsync<TResult>(int? count = null);
public async Task<List<TResult>> GetAllAsync<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllAsync<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllAsync<TKey, TResult>(TKey[] key);
getAllKeys()
public async Task<List<TResult>> GetAllKeysAsync<TResult>(int? count = null);
public async Task<List<TResult>> GetAllKeysAsync<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllKeysAsync<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllKeysAsync<TKey, TResult>(TKey[] key);
Query
public async Task<List<TResult>> QueryAsync<TResult>(string filter, int? count = null, int? skip = null);
public async Task<List<TResult>> QueryAsync<TKey, TResult>(string filter, TKey key, int? count = null, int? skip = null);
public async Task<List<TResult>> QueryAsync<TKey, TResult>(string filter, IDBKeyRange<TKey> key, int? count = null, int? skip = null)
IndexedDbIndex
Properties
name
public string Name
keyPath
public string KeyPath
multiEntry
public bool MultiEntry
unique
public bool Unique
objectStore
public IndexedDbObjectStore ObjectStore
Constructor
public IndexedDbIndex(IndexedDbObjectStore idbStore, string name, string keyPath, bool multiEntry = false, bool unique = false);
Methods
count()
public async Task<int> CountAsync(string indexName);
public async Task<int> CountAsync<TKey>(TKey key);
public async Task<int> CountAsync<TKey>(IDBKeyRange<TKey> key);
get()
public async Task<TResult> GetAsync<TKey, TResult>(TKey queryValue);
getAll()
public async Task<List<TResult>> GetAllAsync<TResult>(int? count = null);
public async Task<List<TResult>> GetAllAsync<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllAsync<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllAsync<TKey, TResult>(TKey[] key);
getKey()
public async Task<TResult> GetKeyAsync<TKey, TResult>(TKey queryValue);
getAllKeys()
public async Task<List<TResult>> GetAllKeysAsync<TResult>(int? count = null);
public async Task<List<TResult>> GetAllKeysAsync<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllKeysAsync<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllKeysAsync<TKey, TResult>(TKey[] key);
Query
public async Task<List<TResult>> QueryAsync<TResult>(string filter, int? count = null, int? skip = null);
public async Task<List<TResult>> QueryAsync<TKey, TResult>(string filter, TKey key, int? count = null, int? skip = null);
public async Task<List<TResult>> QueryAsync<TKey, TResult>(string filter, IDBKeyRange<TKey> key, int? count = null, int? skip = null)
Advanced query functions
The filter expression is the body of a function that receives de parameter obj
than handle each record of ObjectStore.
The function must return an Object of type TResult, that will be included in the List<TResult>
result and can be one of the following options:
- the same object
- a new object
- an array of new objects (unwind)
- undefined (record is not included in result)
for example, return a list of objects that contains the world "per"
in property firstName
ordered using index lastName
.
List<Person> result = await theFactoryDb.Store("people").Index("lastName").Query<Person>(
"if (obj.firstName.toLowerCase().includes('per')) return obj;"
);
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. |
-
net8.0
- BlazorEssentials (>= 3.0.0-CI-20241025-225746)
- Microsoft.JSInterop (>= 8.0.10 && < 10.0.0)
-
net9.0
- BlazorEssentials (>= 3.0.0-CI-20241025-225746)
- Microsoft.JSInterop (>= 9.0.0-rc.2.24474.3 && < 10.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on BlazorEssentials.IndexedDb:
Package | Downloads |
---|---|
SimpleMessageBus.IndexedDb.Core
SimpleMessageBus is a system for making applications more reliable and responsive to users by processing potentially long-running tasks out-of-band from the user's main workflow. It is designed to run either on-prem, or in the Microsoft Cloud, making it suitable for any application, and able to grow as your needs do. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.0.0-CI-20241025-225746 | 34 | 10/26/2024 |
3.0.0-CI-20241025-212513 | 28 | 10/26/2024 |
3.0.0-CI-20241016-025233 | 49 | 10/16/2024 |
3.0.0-CI-20241015-033040 | 35 | 10/15/2024 |
3.0.0-CI-20241011-000041 | 41 | 10/11/2024 |
3.0.0-CI-20240901-165614 | 55 | 9/1/2024 |
3.0.0-CI-20240811-022031 | 72 | 8/11/2024 |
3.0.0-CI-20240810-013656 | 53 | 8/10/2024 |
3.0.0-CI-20240809-035959 | 58 | 8/9/2024 |
3.0.0-CI-20240808-161419 | 55 | 8/8/2024 |
3.0.0-CI-20240723-011915 | 49 | 7/23/2024 |
3.0.0-CI-20240702-173724 | 68 | 7/2/2024 |
3.0.0-CI-20240702-032255 | 44 | 7/2/2024 |
3.0.0-CI-20240701-221235 | 46 | 7/2/2024 |
3.0.0-CI-20240701-162041 | 42 | 7/1/2024 |
3.0.0-CI-20240701-153358 | 37 | 7/1/2024 |