🟨 Slightly different to Blazor WebAssembly
IndexedDB is indeed a powerful NoSQL database that is embedded in modern web browsers. It is designed to provide a client-side storage solution for web applications, allowing developers to store and retrieve large amounts of structured data in a flexible and efficient manner. Unlike traditional relational databases, IndexedDB is a key-value store that allows developers to store data in object stores and access it using indexes. Using IndexedDB can certainly improve website performance, especially when working with large amounts of data.
You can download the example code used in this topic on GitHub.
IndexedDB, as its name suggests, uses indexes to enable high-performance queries. This client-side storage technology allows developers to store various data types as key-value pairs, providing a flexible and versatile way to work with structured data.
IndexedDB offers several useful features, including:
These features make it possible for developers to create efficient, scalable, and reliable web applications that can work with large amounts of data.
In addition to these features, IndexedDB can also help to improve website performance by caching files and large sets of data. By storing data locally, web applications can reduce network latency and improve overall performance, particularly in situations where a network connection may not be reliable or available.
IndexedDB is a client-side storage technology that isolates data by domain, meaning that each domain can only access its own resources. For example, data created by blazorschool.com cannot be accessed by google.com, and vice versa.
IndexedDB organizes data like folders, but with a fixed hierarchy. There are 3 levels in IndexedDB:
In this tutorial, we will provide a basic example of how to store and retrieve values from IndexedDB. Please note that this example will not demonstrate all the possibilities of IndexedDB, but rather serve as an introduction to the basic concepts of this powerful client-side storage technology.
In Firefox, you can find the IndexedDB storage under the Storage tab, as shown in the image below:
In Chrome and Edge, you can find the IndexedDB storage under the Application tab, as shown in the images below:
The IndexedDB storage is exclusively accessible through the JavaScript API. This means that in order to access it, you will need to create a JavaScript module. You can refer to the JavaScript Interaction tutorial and choose IJSRuntime
to interact with your module. Below is an example of how to access the IndexedDB storage using IJSRuntime
.
/js/IndexedDbStorageAccessor.js
export function initialize() { let blazorSchoolIndexedDb = indexedDB.open(DATABASE_NAME, CURRENT_VERSION); blazorSchoolIndexedDb.onupgradeneeded = function () { let db = blazorSchoolIndexedDb.result; db.createObjectStore("books", { keyPath: "id" }); } } let CURRENT_VERSION = 1; let DATABASE_NAME = "Blazor School";
Customize the name of the database by changing the value ofDATABASE_NAME
. You should also increase theCURRENT_VERSION
whenever you change the structure of your data. Additionally, you can create one or more object stores with different names than "books" as shown in the example.
public class IndexedDbAccessor : IAsyncDisposable { private Lazy<IJSObjectReference> _accessorJsRef = new(); private readonly IJSRuntime _jsRuntime; public IndexedDbAccessor(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; } public async Task InitializeAsync() { await WaitForReference(); await _accessorJsRef.Value.InvokeVoidAsync("initialize"); } private async Task WaitForReference() { if (_accessorJsRef.IsValueCreated is false) { _accessorJsRef = new(await _jsRuntime.InvokeAsync<IJSObjectReference>("import", "/js/IndexedDbAccessor.js")); } } public async ValueTask DisposeAsync() { if (_accessorJsRef.IsValueCreated) { await _accessorJsRef.Value.DisposeAsync(); } } }
IndexedDbAccessor
class in the Program.cs file:builder.Services.AddScoped<IndexedDbAccessor>();
@inject IndexedDbAccessor IndexedDbAccessor <Router AppAssembly="@typeof(App).Assembly"> ... </Router> @code { protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await IndexedDbAccessor.InitializeAsync(); } } }
In this section, we will demonstrate how to perform basic IndexedDB storage operations in JavaScript and C#. With the provided code, you can easily store, get, and delete data from the IndexedDB. Additionally, you can customize the code to add your own operations as well.
export function set(collectionName, value) { let blazorSchoolIndexedDb = indexedDB.open(DATABASE_NAME, CURRENT_VERSION); blazorSchoolIndexedDb.onsuccess = function () { let transaction = blazorSchoolIndexedDb.result.transaction(collectionName, "readwrite"); let collection = transaction.objectStore(collectionName) collection.put(value); } } export async function get(collectionName, id) { let request = new Promise((resolve) => { let blazorSchoolIndexedDb = indexedDB.open(DATABASE_NAME, CURRENT_VERSION); blazorSchoolIndexedDb.onsuccess = function () { let transaction = blazorSchoolIndexedDb.result.transaction(collectionName, "readonly"); let collection = transaction.objectStore(collectionName); let result = collection.get(id); result.onsuccess = function (e) { resolve(result.result); } } }); let result = await request; return result; }
WaitForReference()
in all of them. Here is an example implementation:public class IndexedDbAccessor : IAsyncDisposable { ... public async Task<T> GetValueAsync<T>(string collectionName, int id) { await WaitForReference(); var result = await _accessorJsRef.Value.InvokeAsync<T>("get", collectionName, id); return result; } public async Task SetValueAsync<T>(string collectionName, T value) { await WaitForReference(); await _accessorJsRef.Value.InvokeVoidAsync("set", collectionName, value); } }
Once you have completed all the previous steps, you can use the IndexedDB Storage as follows:
@using System.Text.Json @inject IndexedDbAccessor IndexedDbAccessor <form> <label> Book ID: <input type="text" @bind-value="BookId" /> </label> <label> Book Name: <input type="text" @bind-value="BookName" /> </label> <button type="button" @onclick="SetValueAsync">Set Value</button> </form> <div>Stored Value: @StoredValue</div> <button type="button" @onclick="GetValueAsync">Get Value</button> @code { public string BookId { get; set; } = ""; public string BookName { get; set; } = ""; public string StoredValue { get; set; } = ""; public async Task SetValueAsync() { await IndexedDbAccessor.SetValueAsync("books", new { Id = Convert.ToInt32(BookId), Name = BookName }); } public async Task GetValueAsync() { JsonDocument storedBook = await IndexedDbAccessor.GetValueAsync<JsonDocument>("books", Convert.ToInt32(BookId)); StoredValue = storedBook.RootElement.GetProperty("name").GetString() ?? ""; } }
In Blazor WebAssembly, you initialize the IndexedDB at the Program.cs instead of App.razor as with Blazor Server. You can also use InteropServices.JavaScript
to interact with the JavaScript.