Condividi tramite


ASP.NET gestione dello stato lato server core Blazor

Annotazioni

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.

Avvertimento

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Questo articolo descrive gli approcci comuni per la gestione dei dati (stato) di un utente in scenari lato Blazor server.

Mantenere lo stato utente

Il Blazor server-side è un framework per applicazioni stateful. Nella maggior parte dei casi, l'app mantiene una connessione al server. Lo stato dell'utente viene mantenuto nella memoria del server in un circuito.

Esempi di stato utente contenuti in un circuito includono:

  • La gerarchia delle istanze del componente e il relativo output di rendering più recente nell'interfaccia utente resa.
  • Valori dei campi e delle proprietà nelle istanze del componente.
  • Dati contenuti nelle istanze del servizio di iniezione delle dipendenze che sono nell'ambito del circuito.

Lo stato utente può essere trovato anche nelle variabili JavaScript nel set di memoria del browser tramite chiamate di interoperabilità JavaScript.

Se un utente riscontra una perdita temporanea di connessione di rete, Blazor tenta di riconnettere l'utente al circuito originale con lo stato originale. Tuttavia, la riconnessione di un utente al circuito originale nella memoria del server non è sempre possibile:

  • Il server non può mantenere un circuito disconnesso per sempre. Il server deve rilasciare un circuito disconnesso dopo un timeout o quando il server è sotto pressione di memoria.
  • Negli ambienti di distribuzione multiserver con bilanciamento del carico, i singoli server possono avere esito negativo o essere rimossi automaticamente quando non è più necessario gestire il volume complessivo delle richieste. Le richieste di elaborazione del server originali per un utente potrebbero non essere disponibili quando l'utente tenta di riconnettersi.
  • L'utente potrebbe chiudere e riaprire il browser o ricaricare la pagina, rimuovendo qualsiasi stato mantenuto nella memoria del browser. Ad esempio, i valori delle variabili JavaScript impostati tramite chiamate di interoperabilità JavaScript vengono persi.

Quando un utente non può essere riconnesso al circuito originale, l'utente riceve un nuovo circuito con stato appena inizializzato. Equivale alla chiusura e alla riapertura di un'app desktop.

Quando rendere persistente lo stato utente

La persistenza dello stato non è automatica. Per implementare la persistenza dei dati con stato, devi intraprendere delle azioni durante lo sviluppo dell'app.

In genere, mantenere lo stato tra circuiti in cui gli utenti stanno attivamente creando dati e non si limitano a leggere i dati già esistenti.

La persistenza dei dati è in genere necessaria solo per uno stato di valore elevato che gli utenti hanno impiegato sforzi per creare. La persistenza dello stato consente di risparmiare tempo o aiuti nelle attività commerciali:

  • Web form in più passaggi: richiede molto tempo per consentire a un utente di immettere nuovamente i dati per diversi passaggi completati di un modulo Web in più passaggi se il relativo stato viene perso. Un utente perde lo stato in questo scenario se si allontana dal modulo e torna più tardi.
  • Carrello acquisti: qualsiasi componente commerciale importante di un'app che rappresenta potenziali ricavi può essere mantenuto. Un utente che perde il proprio stato, e quindi il carrello acquisti, può acquistare meno prodotti o servizi quando tornano al sito Web in un secondo momento.

Un'app può mantenere solo lo stato dell'app. Le interfacce utente non possono essere rese persistenti, ad esempio le istanze del componente e i relativi alberi di rendering. I componenti e gli alberi di rendering non sono in genere serializzabili. Per rendere persistente lo stato dell'interfaccia utente, ad esempio i nodi espansi di un controllo visualizzazione albero, l'app deve usare codice personalizzato per modellare il comportamento dello stato dell'interfaccia utente come stato dell'app serializzabile.

Persistenza dello stato del circuito

Durante il rendering lato server, Blazor Web Apps può rendere persistente lo stato di sessione (circuito) di un utente quando la connessione al server viene persa per un lungo periodo di tempo o sospesa in modo proattivo, purché non venga attivato un aggiornamento a pagina intera. In questo modo gli utenti possono riprendere la sessione senza perdere il lavoro non salvato negli scenari seguenti:

  • Gestione delle schede del browser
  • Utenti di dispositivi mobili che cambiano app
  • Interruzioni di rete
  • Gestione proattiva delle risorse (sospensione di circuiti inattivi)
  • Spostamento avanzato

Le risorse del server possono essere liberate se lo stato del circuito può essere salvato in modo permanente e quindi ripreso in un secondo momento:

  • Anche se disconnesso, un circuito potrebbe continuare a eseguire operazioni e usare CPU, memoria e altre risorse. Lo stato persistente utilizza solo una quantità fissa di memoria che lo sviluppatore controlla.
  • Lo stato persistente rappresenta un subset della memoria utilizzata dall'app, pertanto il server non deve tenere traccia dei componenti dell'app e di altri oggetti lato server.

Lo stato è persistente per due scenari:

  • Stato del componente: stato utilizzato dai componenti per il rendering di Interactive Server, ad esempio un elenco di elementi recuperati dal database o un modulo compilato dall'utente.
  • Servizi a scopo definito: lo stato è mantenuto all'interno di un servizio lato server, ad esempio l'utente corrente.

Condizioni:

  • La funzionalità è efficace solo per il rendering di Interactive Server.
  • Se l'utente aggiorna la pagina (app), lo stato persistente viene perso.
  • Lo stato deve essere serializzabile JSON. I riferimenti ciclici o le entità ORM potrebbero non serializzare correttamente.
  • Usare @key per garantire l'unicità durante il rendering dei componenti in un ciclo, evitando conflitti di chiave.
  • Mantenere solo lo stato necessario. L'archiviazione di dati eccessivi può influire sulle prestazioni.
  • Nessuna ibernazione automatica. È necessario acconsentire esplicitamente e configurare la persistenza dello stato in modo esplicito.
  • Nessuna garanzia di recupero. Se la persistenza dello stato ha esito negativo, l'app esegue il fallback all'esperienza disconnessa predefinita.

La persistenza dello stato è abilitata per impostazione predefinita quando AddInteractiveServerComponents viene chiamato su AddRazorComponents nel file Program. MemoryCache è l'implementazione di archiviazione predefinita per le singole istanze dell'app e archivia fino a 1.000 circuiti persistenti per due ore, configurabili.

Usare le opzioni seguenti per modificare i valori predefiniti del provider in memoria:

  • PersistedCircuitInMemoryMaxRetained ({CIRCUIT COUNT} segnaposto): numero massimo di circuiti da conservare. Il valore predefinito è 1.000 circuiti. Ad esempio, usare 2000 per mantenere lo stato per un massimo di 2.000 circuiti.
  • PersistedCircuitInMemoryRetentionPeriod ({RETENTION PERIOD} segnaposto): Il periodo massimo di conservazione come TimeSpan. Il valore predefinito è due ore. Ad esempio, usare TimeSpan.FromHours(3) per un periodo di conservazione di tre ore.
services.Configure<CircuitOptions>(options =>
{
    options.PersistedCircuitInMemoryMaxRetained = {CIRCUIT COUNT};
    options.PersistedCircuitInMemoryRetentionPeriod = {RETENTION PERIOD};
});

La persistenza dello stato del componente attraverso i circuiti è basata sull'API esistente PersistentComponentState, che continua a mantenere lo stato per i componenti prerenderizzati che adottano una modalità di rendering interattiva. Per altre informazioni, vedere ASP.NET persistenza dello stato prerendered coreBlazor.

[NOTA] Il salvataggio permanente dello stato del componente per il pre-rendering funziona per qualsiasi modalità di rendering interattiva, ma la persistenza dello stato del circuito funziona solo per la modalità di rendering del server interattivo.

Annotare le proprietà del componente con l'attributo [PersistentState] per abilitare la persistenza dello stato del circuito. Nell'esempio seguente, gli elementi sono contrassegnati dall'attributo di direttiva @key per fornire un identificatore univoco per ogni istanza del componente:

@foreach (var item in Items)
{
    <ItemDisplay @key="@($"unique-prefix-{item.Id}")" Item="item" />
}

@code {
    [PersistentState]
    public List<Item> Items { get; set; }

    protected override async Task OnInitializedAsync()
    {
        Items ??= await LoadItemsAsync();
    }
}

Per rendere persistente lo stato per i servizi con ambito, annotare le proprietà del servizio con l'attributo[PersistentState] , aggiungere il servizio alla raccolta di servizi e chiamare il RegisterPersistentService metodo di estensione con il servizio:

public class CustomUserService
{
    [PersistentState]
    public string UserData { get; set; }
}

services.AddScoped<CustomUserService>();

services.AddRazorComponents()
  .AddInteractiveServerComponents()
  .RegisterPersistentService<CustomUserService>(RenderMode.InteractiveAuto);

[NOTA] L'esempio precedente conserva lo UserData stato quando il servizio viene utilizzato nel prerendering del componente, sia per il rendering tramite Interactive Server che tramite WebAssembly interattivo, perché RenderMode.InteractiveAuto è specificato come RegisterPersistentService. Tuttavia, la persistenza dello stato del circuito è disponibile solo per la modalità di rendering di Interactive Server .

Per gestire la persistenza dello stato distribuito (e per agire come meccanismo di persistenza dello stato predefinito quando configurato), assegnare un HybridCache 'API: HybridCache) all'app, che configura il proprio periodo di persistenza (PersistedCircuitDistributedRetentionPeriodotto ore per impostazione predefinita). HybridCache viene usato perché fornisce un approccio unificato all'archiviazione distribuita che non richiede pacchetti separati per ogni provider di archiviazione.

Nell'esempio seguente viene implementato un oggetto HybridCache con il provider di archiviazione Redis :

services.AddHybridCache()
    .AddRedis("{CONNECTION STRING}");

services.AddRazorComponents()
    .AddInteractiveServerComponents();

Nell'esempio precedente il {CONNECTION STRING} segnaposto rappresenta la stringa di connessione della cache Redis, che deve essere fornita usando un approccio sicuro, ad esempio lo strumento Secret Manager nell'ambiente di sviluppo o Azure Key Vault con identità gestite di Azure per le app distribuite da Azure in qualsiasi ambiente.

Sospendere e riprendere i circuiti

Sospendere e riprendere i circuiti per implementare criteri personalizzati che migliorano la scalabilità di un'app.

La sospensione di un circuito archivia i dettagli sul circuito nell'archiviazione del browser sul lato client e rimuove il circuito, che libera le risorse del server. La ripresa del circuito stabilisce un nuovo circuito e la inizializza usando lo stato persistente.

Da un gestore eventi JavaScript:

  • Chiamare Blazor.pause per sospendere un circuito.
  • Chiamare Blazor.resume per riprendere un circuito.

L'esempio seguente presuppone che un circuito non sia necessario per un'app non visibile:

window.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    Blazor.pause();
  } else if (document.visibilityState === 'visible') {
    Blazor.resume();
  }
});

Mantenere lo stato persistente tra i circuiti

In genere, mantenere lo stato tra circuiti in cui gli utenti stanno attivamente creando dati e non si limitano a leggere i dati già esistenti.

Per mantenere lo stato tra circuiti, l'app deve rendere persistenti i dati in un percorso di archiviazione diverso rispetto alla memoria del server. La persistenza dello stato non è automatica. Per implementare la persistenza dei dati con stato, devi intraprendere delle azioni durante lo sviluppo dell'app.

La persistenza dei dati è in genere necessaria solo per uno stato di valore elevato che gli utenti hanno impiegato sforzi per creare. Negli esempi seguenti, lo stato persistente consente di risparmiare tempo o aiutare in attività commerciali.

  • Web form in più passaggi: richiede molto tempo per consentire a un utente di immettere nuovamente i dati per diversi passaggi completati di un modulo Web in più passaggi se il relativo stato viene perso. Un utente perde lo stato in questo scenario se si allontana dal modulo e torna più tardi.
  • Carrello acquisti: qualsiasi componente commerciale importante di un'app che rappresenta potenziali ricavi può essere mantenuto. Un utente che perde il proprio stato, e quindi il carrello acquisti, può acquistare meno prodotti o servizi quando tornano al sito in un secondo momento.

Un'app può mantenere solo lo stato dell'app. Le interfacce utente non possono essere rese persistenti, ad esempio le istanze del componente e i relativi alberi di rendering. I componenti e gli alberi di rendering non sono in genere serializzabili. Per rendere persistente lo stato dell'interfaccia utente, ad esempio i nodi espansi di un controllo visualizzazione albero, l'app deve usare codice personalizzato per modellare il comportamento dello stato dell'interfaccia utente come stato dell'app serializzabile.

Archiviazione lato server

Per la persistenza permanente dei dati che si estende su più utenti e dispositivi, l'app può usare l'archiviazione lato server. Le opzioni includono:

  • Archiviazione BLOB nel servizio cloud
  • Archiviazione chiave-valore
  • Database relazionale
  • Archiviazione di tabelle

Dopo il salvataggio dei dati, lo stato dell'utente viene conservato e disponibile in qualsiasi nuovo circuito.

Per altre informazioni sulle opzioni di archiviazione dei dati di Azure, vedere quanto segue:

Archiviazione del browser

Per altre informazioni, vedere ASP.NET Gestione dello stato core Blazor con l'archiviazione del browser protetta.

Risorse aggiuntive