This is a Blazor library for accessing IndexedDB, it uses Jake Archibald's idb library for handling access to IndexedDB API.
It tries to implement IndexedDB API with same classes and function names when possible, so you can use public documentation
This library was originally a fork from William Tulloch library Blazor.IndexedDB.
ATTENTION Current 2.3.x versions are development versions, it can suffer major changes between minor version, 2.4.x versions will be considered stable.
Properties
public string Name
public int Version
public List<string> ObjectStoreNames
public IList<IDBObjectStore> ObjectStores
Constructor
public IDBDatabase(IJSRuntime jsRuntime)
Methods
public async Task Open();
public async Task DeleteDatabase();
public IDBObjectStore ObjectStore(string storeName);
Properties
public string Name
public string? KeyPath
public bool AutoIncrement
public IList<IDBIndex> Indexes
public IDBDatabase IDBDatabase
Constructor
public IDBObjectStore(IDBDatabase idbDatabase);
Methods
public async Task Add<TData>(TData data);
public async Task Add<TData, TKey>(TData data, TKey key);
public async Task Put<TData>(TData data);
public async Task Put<TData, TKey>(TData data, TKey key);
public async Task Delete<TKey>(TKey key);
public async Task ClearStore();
public async Task BatchAdd<TData>(TData[] data);
public async Task BatchPut<TData>(TData[] data);
public async Task BatchDelete<TKey>(TKey[] key);
public async Task<int> Count();
public async Task<int> Count<TKey>(TKey key);
public async Task<int> Count<TKey>(IDBKeyRange<TKey> key);
public async Task<TResult?> Get<TKey, TResult>(TKey key);
public async Task<List<TResult>> GetAll<TResult>(int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey[] key);
public async Task<List<TResult>> GetAllKeys<TResult>(int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey[] key);
public async Task<List<TResult>> Query<TResult>(string filter, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, TKey key, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, IDBKeyRange<TKey> key, int? count = null, int? skip = null)
Properties
public string Name
public string KeyPath
public bool MultiEntry
public bool Unique
public IDBObjectStore ObjectStore
Constructor
public IDBIndex(IDBObjectStore idbStore, string name, string keyPath, bool multiEntry = false, bool unique = false);
Methods
public async Task<int> Count(string indexName);
public async Task<int> Count<TKey>(TKey key);
public async Task<int> Count<TKey>(IDBKeyRange<TKey> key);
public async Task<TResult> Get<TKey, TResult>(TKey queryValue);
public async Task<List<TResult>> GetAll<TResult>(int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAll<TKey, TResult>(TKey[] key);
public async Task<TResult> GetKey<TKey, TResult>(TKey queryValue);
public async Task<List<TResult>> GetAllKeys<TResult>(int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(IDBKeyRange<TKey> key, int? count = null);
public async Task<List<TResult>> GetAllKeys<TKey, TResult>(TKey[] key);
public async Task<List<TResult>> Query<TResult>(string filter, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, TKey key, int? count = null, int? skip = null);
public async Task<List<TResult>> Query<TKey, TResult>(string filter, IDBKeyRange<TKey> key, int? count = null, int? skip = null)
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;"
);
Check simple Blazor WASM PWA demo in Demos BlazorIndexedDbJsClientDemo
NET 5.0 or newer
Install-Package BlazorIndexedDbJs
or
dotnet add package BlazorIndexedDbJs
For blazor wasm, in wwwroot\index.html
...
<body>
...
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/BlazorIndexedDbJs/BlazorIndexedDb.js"></script>
</body>
For blazor server, in Pages/_Host.cshtml
...
<body>
...
<script src="_framework/blazor.server.js"></script>
<script src="_content/BlazorIndexedDbJs/BlazorIndexedDb.js"></script>
</body>
Although it is not mandatory, because ObjectStores and Indexes can be accessed by name, it is preferable to define the database schema using classes, this facilitates refactoring and detecting errors at compile time.
Without classes
var people = await theFactoryDb.Store("Employees").Index("firstName").GetAll<Person>();
With classes
var people = await theFactoryDb.Employees.FirstName.GetAll<Person>();
Example of schema using classes:
Data/TheFactoryDb.cs
using System.Collections.Generic;
using Microsoft.JSInterop;
using BlazorIndexedDbJs;
namespace BlazorIndexedDbJsClientDemo.Data
{
public class Employees: IDBObjectStore
{
public IDBIndex FirstName { get; }
public IDBIndex LastName { get; }
public IDBIndex FullName { get; }
public Employees(IDBDatabase database): base(database)
{
Name = "Employees";
KeyPath = "id";
AutoIncrement = true;
FirstName = new IDBIndex(this, "firstName", "firstName");
LastName = new IDBIndex(this, "lastName", "lastName");
FullName = new IDBIndex(this, "fullName", "firstName,lastName");
}
}
public class TheFactoryDb: IDBDatabase
{
public Employees Employees { get; }
public TheFactoryDb(IJSRuntime jsRuntime): base(jsRuntime)
{
Name = "TheFactory";
Version = 1;
Employees = new Employees(this);
}
}
}
For blazor wasm, 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) });
builder.Services.AddScoped<TheFactoryDb>();
await builder.Build().RunAsync();
}
}
For blazor server, in startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
services.AddScoped<TheFactoryDb>();
}
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; }
}
And the data store name is "Employees"
To use IndexedDB in a component or page, first inject the IDBDatabase instance.
@inject TheFactoryDb theFactoryDb
This will create the database if it not exists and will upgrade schema to new version if it is older.
await theFactoryDb.Open()
var people = await theFactoryDb.Employees.GetAll<Person>();
var person = await theFactoryDb.Employees.Get<long, Person>(id);
var person = await theFactoryDb.Employees.FirstName.Get<string, Person>("John");
var people = await theFactoryDb.Employees.FirstName.GetAll<string, Person>("John");
var newPerson = new Person() {
FirstName = "John",
LastName = "Doe"
};
await theFactoryDb.Employees.Add(newPerson);
await theFactoryDb.Employees.Put<Person>(recordToUpdate)
await theFactoryDb.Employees.Delete<int>(id)
await theFactoryDb.Employees.Clear()
await theFactoryDb.DeleteDb()