Offline first con Blazor e IndexedDB

di Morgan Pizzini, in ASP.NET Core,

Blazor è il primo framework puramente front-end sviluppato da Microsoft, nato rispettando i più moderni standard web, tra questi la possibilità di essere installato su un dispositivo emulando un'applicazione vera e propria. Questo approccio, chiamato PWA, è configurabile già in fase di creazione del progetto, spuntando una checkbox. Benche in alcuni casi sia più che sufficiente, in realtà non è che l'inizio!

In un'applicazione sviluppata per essere una PWA bisogna tenere in considerazione che l'utente potrebbe accedere in modalità offline oppure in un luogo in cui non vi è connettività, al che l'unica cosa che vedrà sarà l'interfaccia, perchè i dati, essendo recuperati tramite servizi remoti, non saranno disponibili. Da qui nasce la necessità di cambiare approccio di sviluppo: parliamo di creare applicazioni in modalità Offline First, dove i dati, o una parte di essi, una volta scaricati, vengono salvati all'interno del browser e sincronizzati successivamente con il server.

Un luogo di allocazione può essere il LocalStorage: come abbiamo visto negli scorsi script, in cui salvavamo le scelte di configurazione dell'utente, ma esso ha un limite di memoria pari a 5MB, e non possiede buone prestazioni in lettura/scrittura. Potremmo usare il SessionStorage che ha una memoria illimitata, ma volatile, quindi non adatta.

A lato di queste tecnologie che permettono un salvataggio locale troviamo IndexedDB, che potremmo definire un database integrato nel browser, nel quale possiamo salvare qualsiasi tipologia ed effettuare query in lettura.

L'implementazione in Blazor parte dall'utilizzo della libreria DnetIndexedDb installabile tramite NuGet, e di una libreria custom, allegata a questo script, contenente una serie di metodi che facilitano l'interfacciamento verso IndexedDB.

<ItemGroup>
    <.. />
    <PackageReference Include="DnetIndexedDb" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
    <ProjectReference Include="..\IndexDbHelpers\IndexDbHelpers.csproj" />
</ItemGroup>

Il secondo passo consiste nella creazione di un contesto, così come avviene per Entity framework, estendendo la classe IndexedDbInterop ed un modello, al quale applicare gli attributi necessari per effetture le query

public class ApplicationIndexDb : IndexedDbInterop
{
    public ApplicationIndexDb(IJSRuntime jsRuntime, IndexedDbOptions<ApplicationIndexDb> options)
        : base(jsRuntime, options)
    {
    }
}

public class SampleContract
{
    [IndexDbKey]
    public string Id { get; set; }
    [IndexDbIndex]
    public string Name { get; set; }
}

È importante notare che il modello non è stato definito all'interno del contesto, infatti l'assegnazione avverrà nel file Program.cs, nel quale inseriremo tra i servizi l'accesso al database IndexedDB, al cui interno vi sarà una tabella per SampleContract.

builder.Services.AddIndexedDbDatabase<ApplicationIndexDb>(options =>
{
    var indexedDbDatabaseModel = new IndexedDbDatabaseModel()
        // nome
        .WithName(nameof(ApplicationIndexDb))
        // versione
        .WithVersion(1);
    
    // tabelle
    indexedDbDatabaseModel.AddStore<SampleContract>();

    options.UseDatabase(indexedDbDatabaseModel);
});

A questo punto nella pagina, o servizio, designato possiamo accedere al database tramite dependecy injection.

@page "/samples"
@using IndexDbHelpers
@inject ApplicationIndexDb context

<p>Current count: @list.Count</p>

<button class="btn btn-primary" @onclick="Add">Add!</button>
@code {
    private IList<SampleContract> list = new List<SampleContract>();
    
    private async Task Add()
    {
        // connessione al DB
        var openResult = await context.OpenIndexedDb();

        // aggiunta item
        await context.AddItems(new List<SampleContract> 
        {
          new() { Id = Guid.NewGuid().ToString(), Name = $"Contract {DateTime.Now}" } 
        });

        // lettura items
        list = await context.GetAll<SampleContract>();
    }
}

Infine, dobbiamo ricordare che tutte le operazioni, che dialogano con IndexedDB, utilizzano Interop Javascript, è quindi necessario importare lo script per gestire queste chiamate. All'interno di index.html aggiungiamo

<script src="_content/DnetIndexedDb/rxjs.min.js"></script>
<script src="_content/DnetIndexedDb/dnet-indexeddb.js"></script>

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti

I più letti di oggi