ASP.NET Core Razor-Komponentenlebenszyklus

In diesem Artikel werden der ASP.NET Core Razor-Komponentenlebenszyklus und die Verwendung von Lebenszyklusereignissen erläutert.

Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.

Lebenszyklusereignisse

Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.

Ereignisse des Komponentenlebenszyklus:

  1. Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
    • Erstellen Sie die Instanz der Komponente.
    • Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus SetParametersAsync.
    • Rufen Sie OnInitialized{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  2. Rufen Sie OnParametersSet{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Hinweis

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.

Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Komponentenlebenszyklusereignisse einer Razor-Komponente in Blazor

Verarbeitung von DOM-Ereignissen (Document Object Model):

  1. Der Ereignishandler wird ausgeführt.
  2. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Verarbeitung von DOM-Ereignissen (Document Object Model)

Der Render-Lebenszyklus:

  1. Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
  2. Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
  3. Warten Sie, bis das DOM aktualisiert wurde.
  4. Rufen Sie OnAfterRender{Async} auf.

Lebenszyklus von Rendervorgängen

Wenn StateHasChanged von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Wenn Parameter festgelegt wurden (SetParametersAsync)

SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.

Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.

Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]- oder [CascadingParameter]-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.

Wenn base.SetParametersAsync nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param-Parameters value zu, wenn die Analyse eines Routingparameters für Param erfolgreich ist. Wenn value nicht null lautet, wird der Wert von der Komponente angezeigt.

Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?} in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?} abzurufen. Wenn /{param?} in diesem Szenario verwendet wird, gibt TryGetValuefalse zurück, und message wird auf keine der message-Zeichenfolgen festgelegt.

Pages/SetParamsAsync.razor:

@page "/set-params-async/{Param?}"

<p>@message</p>

@code {
    private string message = "Not set";

    [Parameter]
    public string? Param { get; set; }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        if (parameters.TryGetValue<string>(nameof(Param), out var value))
        {
            if (value is null)
            {
                message = "The value of 'Param' is null.";
            }
            else
            {
                message = $"The value of 'Param' is {value}.";
            }
        }

        await base.SetParametersAsync(parameters);
    }
}

Komponenteninitialisierung (OnInitialized{Async})

OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.

Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:

Pages/OnInit.razor:

@page "/on-init"

<p>@message</p>

@code {
    private string? message;

    protected override void OnInitialized()
    {
        message = $"Initialized at {DateTime.Now}";
    }
}

Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await, um einen asynchronen Vorgang durchzuführen:

protected override async Task OnInitializedAsync()
{
    await ...
}

Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:

  • Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
  • Ein zweites Mal, wenn der Browser die Komponente rendert.

Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Nachdem Parameter festgelegt wurden (OnParametersSet{Async})

OnParametersSet oder OnParametersSetAsync wird aufgerufen:

  • Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.

  • Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:

    • Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
    • Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.

    Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:

  • Mit einem Startdatum, das von StartDate empfangen wird: /on-parameters-set/2021-03-19
  • Ohne Startdatum, wobei StartDate einem Wert der aktuellen Ortszeit zugewiesen wird: /on-parameters-set

Pages/OnParamsSet.razor:

Hinweis

In einer Komponentenroute ist es nicht möglich, einen DateTime-Parameter mit der Routenbeschränkung datetime einzuschränken und den Parameter optional zu machen. Daher verwendet die folgende OnParamsSet-Komponente zwei @page-Anweisungen, um das Routing mit und ohne ein bereitgestelltes Datumssegment in der URL zu verarbeiten.

@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"

<p>@message</p>

@code {
    private string? message;

    [Parameter]
    public DateTime StartDate { get; set; }

    protected override void OnParametersSet()
    {
        if (StartDate == default)
        {
            StartDate = DateTime.Now;

            message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
        }
        else
        {
            message = $"The start date in the URL was used (StartDate: {StartDate}).";
        }
    }
}

Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnParametersSetAsync()
{
    await ...
}

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.

Nach dem Rendern der Komponente (OnAfterRender{Async})

OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.

Der Parameter firstRender für OnAfterRender und OnAfterRenderAsync:

  • Wird auf true festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird.
  • Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.

Pages/AfterRender.razor:

@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger 

<button @onclick="LogInformation">Log information (and trigger a render)</button>

@code {
    private string message = "Initial assigned message.";

    protected override void OnAfterRender(bool firstRender)
    {
        Logger.LogInformation("OnAfterRender(1): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);

        if (firstRender)
        {
            message = "Executed for the first render.";
        }
        else
        {
            message = "Executed after the first render.";
        }

        Logger.LogInformation("OnAfterRender(2): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);
    }

    private void LogInformation()
    {
        Logger.LogInformation("LogInformation called");
    }
}

Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.

OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendern interaktiv gerendert wird. Beim Vorabrendern der App:

  1. Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
  2. Wenn das Blazor-Skript (blazor.server.js oder blazor.webassembly.js) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Statusänderungen (StateHasChanged)

StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.

StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.

Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.

Behandeln unvollständiger asynchroner Aktionen beim Rendern

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null sind.

In der FetchData-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts). Wenn forecasts gleich null ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.

Pages/FetchData.razor in der Blazor Server-Vorlage:

@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateOnly.FromDateTime(DateTime.Now));
    }
}

Behandeln von Fehlern

Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.

Zustandsbehaftete erneute Verbindung nach dem Prerendering

Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:

  • Wenn die Komponente statisch vorab gerendert ist.
  • Nachdem die Serververbindung hergestellt wurde.

Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.

Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync-Methode zurückgegeben werden.

WeatherForecastService.cs:

using Microsoft.Extensions.Caching.Memory;

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }

    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]?> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow =
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}

Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.

Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Voarabrendering mit JavaScript-Interop

Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.

Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).

Im folgenden Beispiel wird die Funktion setElementText1 im Element <head> platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Das OnAfterRender{Async}-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.

Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText1 = (element, text) => element.innerText = text;

Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.

Im folgenden Beispiel wird die Funktion setElementText2 im Element <head> platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.

StateHasChanged wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged nur aufgerufen wird, wenn datanull ist.

Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string? data;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && data == null)
        {
            data = await JS.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

Beseitigung von Komponenten mit IDisposable und IAsyncDisposable

Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.

Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.

Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.

Löschen von Objektverweisen bei JavaScript-Interoperabilität

Alle Beispiele im Artikel zur JavaScript-Interoperabilität (JS) veranschaulichen typische Muster zum Löschen von Objekten:

Objektverweise bei JS-Interoperabilität werden als Zuordnung implementiert, die durch einen Bezeichner auf der Seite des JS-Interoperabilitätsaufrufs definiert ist, der den Verweis erstellt. Wenn die Objektlöschung von .NET oder JS aus initiiert wird, entfernt Blazor den Eintrag aus der Zuordnung, und das Objekt kann durch die Garbage Collection gelöscht werden, sofern kein anderer starker Verweis auf das Objekt vorhanden ist.

Sie sollten immer mindestens die auf .NET-Seite erstellten Objekte löschen, um Verluste im von .NET verwalteten Arbeitsspeicher zu vermeiden.

DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten

Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver-Muster in JavaScript auf dem Client:

  • Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in Dispose{Async} aus dem DOM entfernt.
  • In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in Dispose{Async} ausgeführt wird.

Mit dem Muster MutationObserver können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.

Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.

Synchrone IDisposable

Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable

...

@code {
    ...

    public void Dispose()
    {
        obj?.Dispose();
    }
}

Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.

Pages/CounterWithTimerDisposal1.razor:

@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}

Hinweis

Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized/OnInitializedAsyncnullDispose erstellt wird.

Pages/CounterWithTimerDisposal2.razor:

@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer? timer;

    protected override void OnInitialized()
    {
        timer = new Timer(1000);
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer?.Dispose();
}

Weitere Informationen finden Sie unter

Asynchrone IAsyncDisposable

Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IAsyncDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IAsyncDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable

...

@code {
    ...

    public async ValueTask DisposeAsync()
    {
        if (obj is not null)
        {
            await obj.DisposeAsync();
        }
    }
}

Weitere Informationen finden Sie unter

Zuweisung von null zu beseitigten Objekten

In der Regel ist es nicht notwendig, null freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null zugewiesen werden muss, gehören:

  • Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie null nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen.
  • Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von null das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.

Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null zuzuweisen. In den seltenen Fällen, in denen null einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null zugewiesen werden muss.

StateHasChanged

Hinweis

Der Aufruf von StateHasChanged in Dispose wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.

Ereignishandler

Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose-Methode:

  • Ansatz mit einem privatem Feld und Lambdaausdruck

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
    
        private EventHandler<FieldChangedEventArgs>? fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new(model);
    
            fieldChanged = (_, __) =>
            {
                ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • Ansatz mit einer privaten Methode

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
    
        protected override void OnInitialized()
        {
            editContext = new(model);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    

Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable und IAsyncDisposable.

Anonyme Funktionen, Methoden und Ausdrücke

Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.

  • Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):

    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        formInvalid = !editContext.Validate();
        StateHasChanged();
    }
    
    protected override void OnInitialized()
    {
        editContext = new(starship);
        editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
    }
    
  • Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):

    private ValidationMessageStore? messageStore;
    
    [CascadingParameter]
    private EditContext? CurrentEditContext { get; set; }
    
    protected override void OnInitialized()
    {
        ...
    
        messageStore = new(CurrentEditContext);
    
        CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
        CurrentEditContext.OnFieldChanged += (s, e) => 
            messageStore.Clear(e.FieldIdentifier);
    }
    

    Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.

Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose und DisposeAsync.

Abbrechbare Hintergrundarbeit

Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.

Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:

  • Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
  • Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
  • Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
  • Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
  • Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.

So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:

Im folgenden Beispiel:

  • await Task.Delay(5000, cts.Token); stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.
  • BackgroundResourceMethod stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn die Resource vor dem Aufruf der Methode verworfen wird.

Pages/BackgroundWork.razor:

@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger

<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>

@code {
    private Resource resource = new();
    private CancellationTokenSource cts = new();

    protected async Task LongRunningWork()
    {
        Logger.LogInformation("Long running work started");

        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod(Logger);
    }

    public void Dispose()
    {
        Logger.LogInformation("Executing Dispose");
        cts.Cancel();
        cts.Dispose();
        resource?.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
        {
            logger.LogInformation("BackgroundResourceMethod: Start method");

            if (disposed)
            {
                logger.LogInformation("BackgroundResourceMethod: Disposed");
                throw new ObjectDisposedException(nameof(Resource));
            }

            // Take action on the Resource

            logger.LogInformation("BackgroundResourceMethod: Action on Resource");
        }

        public void Dispose()
        {
            disposed = true;
        }
    }
}

Blazor Server-Ereignisse zur Wiederherstellung von Verbindungen

Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.

Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.

Lebenszyklusereignisse

Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.

Ereignisse des Komponentenlebenszyklus:

  1. Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
    • Erstellen Sie die Instanz der Komponente.
    • Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus SetParametersAsync.
    • Rufen Sie OnInitialized{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  2. Rufen Sie OnParametersSet{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Hinweis

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.

Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Komponentenlebenszyklusereignisse einer Razor-Komponente in Blazor

Verarbeitung von DOM-Ereignissen (Document Object Model):

  1. Der Ereignishandler wird ausgeführt.
  2. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Verarbeitung von DOM-Ereignissen (Document Object Model)

Der Render-Lebenszyklus:

  1. Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
  2. Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
  3. Warten Sie, bis das DOM aktualisiert wurde.
  4. Rufen Sie OnAfterRender{Async} auf.

Lebenszyklus von Rendervorgängen

Wenn StateHasChanged von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Wenn Parameter festgelegt wurden (SetParametersAsync)

SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.

Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.

Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]- oder [CascadingParameter]-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.

Wenn base.SetParametersAsync nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param-Parameters value zu, wenn die Analyse eines Routingparameters für Param erfolgreich ist. Wenn value nicht null lautet, wird der Wert von der Komponente angezeigt.

Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?} in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?} abzurufen. Wenn /{param?} in diesem Szenario verwendet wird, gibt TryGetValuefalse zurück, und message wird auf keine der message-Zeichenfolgen festgelegt.

Pages/SetParamsAsync.razor:

@page "/set-params-async/{Param?}"

<p>@message</p>

@code {
    private string message = "Not set";

    [Parameter]
    public string? Param { get; set; }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        if (parameters.TryGetValue<string>(nameof(Param), out var value))
        {
            if (value is null)
            {
                message = "The value of 'Param' is null.";
            }
            else
            {
                message = $"The value of 'Param' is {value}.";
            }
        }

        await base.SetParametersAsync(parameters);
    }
}

Komponenteninitialisierung (OnInitialized{Async})

OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.

Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:

Pages/OnInit.razor:

@page "/on-init"

<p>@message</p>

@code {
    private string? message;

    protected override void OnInitialized()
    {
        message = $"Initialized at {DateTime.Now}";
    }
}

Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await, um einen asynchronen Vorgang durchzuführen:

protected override async Task OnInitializedAsync()
{
    await ...
}

Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:

  • Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
  • Ein zweites Mal, wenn der Browser die Komponente rendert.

Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Nachdem Parameter festgelegt wurden (OnParametersSet{Async})

OnParametersSet oder OnParametersSetAsync wird aufgerufen:

  • Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.

  • Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:

    • Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
    • Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.

    Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:

  • Mit einem Startdatum, das von StartDate empfangen wird: /on-parameters-set/2021-03-19
  • Ohne Startdatum, wobei StartDate einem Wert der aktuellen Ortszeit zugewiesen wird: /on-parameters-set

Pages/OnParamsSet.razor:

Hinweis

In einer Komponentenroute ist es nicht möglich, einen DateTime-Parameter mit der Routenbeschränkung datetime einzuschränken und den Parameter optional zu machen. Daher verwendet die folgende OnParamsSet-Komponente zwei @page-Anweisungen, um das Routing mit und ohne ein bereitgestelltes Datumssegment in der URL zu verarbeiten.

@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"

<p>@message</p>

@code {
    private string? message;

    [Parameter]
    public DateTime StartDate { get; set; }

    protected override void OnParametersSet()
    {
        if (StartDate == default)
        {
            StartDate = DateTime.Now;

            message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
        }
        else
        {
            message = $"The start date in the URL was used (StartDate: {StartDate}).";
        }
    }
}

Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnParametersSetAsync()
{
    await ...
}

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.

Nach dem Rendern der Komponente (OnAfterRender{Async})

OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.

Der Parameter firstRender für OnAfterRender und OnAfterRenderAsync:

  • Wird auf true festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird.
  • Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.

Pages/AfterRender.razor:

@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger 

<button @onclick="LogInformation">Log information (and trigger a render)</button>

@code {
    private string message = "Initial assigned message.";

    protected override void OnAfterRender(bool firstRender)
    {
        Logger.LogInformation("OnAfterRender(1): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);

        if (firstRender)
        {
            message = "Executed for the first render.";
        }
        else
        {
            message = "Executed after the first render.";
        }

        Logger.LogInformation("OnAfterRender(2): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);
    }

    private void LogInformation()
    {
        Logger.LogInformation("LogInformation called");
    }
}

Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.

OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendering gerendert und interaktiv ist. Beim Vorabrendern der App:

  1. Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
  2. Wenn das Blazor-Skript (blazor.server.js oder blazor.webassembly.js) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Statusänderungen (StateHasChanged)

StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.

StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.

Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.

Behandeln unvollständiger asynchroner Aktionen beim Rendern

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null sind.

In der FetchData-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts). Wenn forecasts gleich null ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.

Pages/FetchData.razor in der Blazor Server-Vorlage:

@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Behandeln von Fehlern

Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.

Zustandsbehaftete erneute Verbindung nach dem Prerendering

Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:

  • Wenn die Komponente statisch vorab gerendert ist.
  • Nachdem die Serververbindung hergestellt wurde.

Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.

Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync-Methode zurückgegeben werden.

WeatherForecastService.cs:

using Microsoft.Extensions.Caching.Memory;

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }

    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow =
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}

Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.

Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Voarabrendering mit JavaScript-Interop

Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.

Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).

Im folgenden Beispiel wird die Funktion setElementText1 im Element <head> platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Das OnAfterRender{Async}-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.

Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText1 = (element, text) => element.innerText = text;

Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.

Im folgenden Beispiel wird die Funktion setElementText2 im Element <head> platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.

StateHasChanged wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged nur aufgerufen wird, wenn datanull ist.

Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string? data;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && data == null)
        {
            data = await JS.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

Beseitigung von Komponenten mit IDisposable und IAsyncDisposable

Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.

Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.

Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.

DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten

Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver-Muster in JavaScript auf dem Client:

  • Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in Dispose{Async} aus dem DOM entfernt.
  • In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in Dispose{Async} ausgeführt wird.

Mit dem Muster MutationObserver können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.

Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.

Synchrone IDisposable

Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable

...

@code {
    ...

    public void Dispose()
    {
        obj?.Dispose();
    }
}

Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.

Pages/CounterWithTimerDisposal1.razor:

@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}

Hinweis

Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized/OnInitializedAsyncnullDispose erstellt wird.

Pages/CounterWithTimerDisposal2.razor:

@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer? timer;

    protected override void OnInitialized()
    {
        timer = new Timer(1000);
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer?.Dispose();
}

Weitere Informationen finden Sie unter

Asynchrone IAsyncDisposable

Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IAsyncDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IAsyncDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable

...

@code {
    ...

    public async ValueTask DisposeAsync()
    {
        if (obj is not null)
        {
            await obj.DisposeAsync();
        }
    }
}

Weitere Informationen finden Sie unter

Zuweisung von null zu beseitigten Objekten

In der Regel ist es nicht notwendig, null freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null zugewiesen werden muss, gehören:

  • Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie null nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen.
  • Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von null das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.

Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null zuzuweisen. In den seltenen Fällen, in denen null einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null zugewiesen werden muss.

StateHasChanged

Hinweis

Der Aufruf von StateHasChanged in Dispose wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.

Ereignishandler

Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose-Methode:

  • Ansatz mit einem privatem Feld und Lambdaausdruck

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
    
        private EventHandler<FieldChangedEventArgs>? fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new(model);
    
            fieldChanged = (_, __) =>
            {
                ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • Ansatz mit einer privaten Methode

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        ...
    
        protected override void OnInitialized()
        {
            editContext = new(model);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    

Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable und IAsyncDisposable.

Anonyme Funktionen, Methoden und Ausdrücke

Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.

  • Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):

    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        formInvalid = !editContext.Validate();
        StateHasChanged();
    }
    
    protected override void OnInitialized()
    {
        editContext = new(starship);
        editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
    }
    
  • Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):

    private ValidationMessageStore? messageStore;
    
    [CascadingParameter]
    private EditContext? CurrentEditContext { get; set; }
    
    protected override void OnInitialized()
    {
        ...
    
        messageStore = new(CurrentEditContext);
    
        CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
        CurrentEditContext.OnFieldChanged += (s, e) => 
            messageStore.Clear(e.FieldIdentifier);
    }
    

    Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.

Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose und DisposeAsync.

Abbrechbare Hintergrundarbeit

Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.

Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:

  • Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
  • Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
  • Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
  • Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
  • Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.

So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:

Im folgenden Beispiel:

  • await Task.Delay(5000, cts.Token); stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.
  • BackgroundResourceMethod stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn die Resource vor dem Aufruf der Methode verworfen wird.

Pages/BackgroundWork.razor:

@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger

<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>

@code {
    private Resource resource = new();
    private CancellationTokenSource cts = new();

    protected async Task LongRunningWork()
    {
        Logger.LogInformation("Long running work started");

        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod(Logger);
    }

    public void Dispose()
    {
        Logger.LogInformation("Executing Dispose");
        cts.Cancel();
        cts.Dispose();
        resource?.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
        {
            logger.LogInformation("BackgroundResourceMethod: Start method");

            if (disposed)
            {
                logger.LogInformation("BackgroundResourceMethod: Disposed");
                throw new ObjectDisposedException(nameof(Resource));
            }

            // Take action on the Resource

            logger.LogInformation("BackgroundResourceMethod: Action on Resource");
        }

        public void Dispose()
        {
            disposed = true;
        }
    }
}

Blazor Server-Ereignisse zur Wiederherstellung von Verbindungen

Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.

Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.

Lebenszyklusereignisse

Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.

Ereignisse des Komponentenlebenszyklus:

  1. Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
    • Erstellen Sie die Instanz der Komponente.
    • Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus SetParametersAsync.
    • Rufen Sie OnInitialized{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  2. Rufen Sie OnParametersSet{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Hinweis

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.

Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Komponentenlebenszyklusereignisse einer Razor-Komponente in Blazor

Verarbeitung von DOM-Ereignissen (Document Object Model):

  1. Der Ereignishandler wird ausgeführt.
  2. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Verarbeitung von DOM-Ereignissen (Document Object Model)

Der Render-Lebenszyklus:

  1. Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
  2. Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
  3. Warten Sie, bis das DOM aktualisiert wurde.
  4. Rufen Sie OnAfterRender{Async} auf.

Lebenszyklus von Rendervorgängen

Wenn StateHasChanged von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Wenn Parameter festgelegt wurden (SetParametersAsync)

SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.

Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.

Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]- oder [CascadingParameter]-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.

Wenn base.SetParametersAsync nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param-Parameters value zu, wenn die Analyse eines Routingparameters für Param erfolgreich ist. Wenn value nicht null lautet, wird der Wert von der Komponente angezeigt.

Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?} in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?} abzurufen. Wenn /{param?} in diesem Szenario verwendet wird, gibt TryGetValuefalse zurück, und message wird auf keine der message-Zeichenfolgen festgelegt.

Pages/SetParamsAsync.razor:

@page "/set-params-async/{Param?}"

<p>@message</p>

@code {
    private string message = "Not set";

    [Parameter]
    public string Param { get; set; }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        if (parameters.TryGetValue<string>(nameof(Param), out var value))
        {
            if (value is null)
            {
                message = "The value of 'Param' is null.";
            }
            else
            {
                message = $"The value of 'Param' is {value}.";
            }
        }

        await base.SetParametersAsync(parameters);
    }
}

Komponenteninitialisierung (OnInitialized{Async})

OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.

Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:

Pages/OnInit.razor:

@page "/on-init"

<p>@message</p>

@code {
    private string message;

    protected override void OnInitialized()
    {
        message = $"Initialized at {DateTime.Now}";
    }
}

Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await, um einen asynchronen Vorgang durchzuführen:

protected override async Task OnInitializedAsync()
{
    await ...
}

Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:

  • Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
  • Ein zweites Mal, wenn der Browser die Komponente rendert.

Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Nachdem Parameter festgelegt wurden (OnParametersSet{Async})

OnParametersSet oder OnParametersSetAsync wird aufgerufen:

  • Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.

  • Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:

    • Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
    • Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.

    Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:

  • Mit einem Startdatum, das von StartDate empfangen wird: /on-parameters-set/2021-03-19
  • Ohne Startdatum, wobei StartDate einem Wert der aktuellen Ortszeit zugewiesen wird: /on-parameters-set

Pages/OnParamsSet.razor:

Hinweis

In einer Komponentenroute ist es nicht möglich, einen DateTime-Parameter mit der Routenbeschränkung datetime einzuschränken und den Parameter optional zu machen. Daher verwendet die folgende OnParamsSet-Komponente zwei @page-Anweisungen, um das Routing mit und ohne ein bereitgestelltes Datumssegment in der URL zu verarbeiten.

@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"

<p>@message</p>

@code {
    private string message;

    [Parameter]
    public DateTime StartDate { get; set; }

    protected override void OnParametersSet()
    {
        if (StartDate == default)
        {
            StartDate = DateTime.Now;

            message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
        }
        else
        {
            message = $"The start date in the URL was used (StartDate: {StartDate}).";
        }
    }
}

Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnParametersSetAsync()
{
    await ...
}

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.

Nach dem Rendern der Komponente (OnAfterRender{Async})

OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.

Der Parameter firstRender für OnAfterRender und OnAfterRenderAsync:

  • Wird auf true festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird.
  • Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.

Pages/AfterRender.razor:

@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger 

<button @onclick="LogInformation">Log information (and trigger a render)</button>

@code {
    private string message = "Initial assigned message.";

    protected override void OnAfterRender(bool firstRender)
    {
        Logger.LogInformation("OnAfterRender(1): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);

        if (firstRender)
        {
            message = "Executed for the first render.";
        }
        else
        {
            message = "Executed after the first render.";
        }

        Logger.LogInformation("OnAfterRender(2): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);
    }

    private void LogInformation()
    {
        Logger.LogInformation("LogInformation called");
    }
}

Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.

OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendering gerendert und interaktiv ist. Beim Vorabrendern der App:

  1. Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
  2. Wenn das Blazor-Skript (blazor.server.js oder blazor.webassembly.js) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Statusänderungen (StateHasChanged)

StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.

StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.

Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.

Behandeln unvollständiger asynchroner Aktionen beim Rendern

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null sind.

In der FetchData-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts). Wenn forecasts gleich null ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.

Pages/FetchData.razor in der Blazor Server-Vorlage:

@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Behandeln von Fehlern

Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.

Zustandsbehaftete erneute Verbindung nach dem Prerendering

Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:

  • Wenn die Komponente statisch vorab gerendert ist.
  • Nachdem die Serververbindung hergestellt wurde.

Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.

Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync-Methode zurückgegeben werden.

WeatherForecastService.cs:

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }

    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow =
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}

Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.

Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Voarabrendering mit JavaScript-Interop

Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.

Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).

Im folgenden Beispiel wird die Funktion setElementText1 im Element <head> platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Das OnAfterRender{Async}-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.

Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText1 = (element, text) => element.innerText = text;

Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.

Im folgenden Beispiel wird die Funktion setElementText2 im Element <head> platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.

StateHasChanged wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged nur aufgerufen wird, wenn datanull ist.

Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string? data;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && data == null)
        {
            data = await JS.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

Beseitigung von Komponenten mit IDisposable und IAsyncDisposable

Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.

Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.

Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.

DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten

Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver-Muster in JavaScript auf dem Client:

  • Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in Dispose{Async} aus dem DOM entfernt.
  • In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in Dispose{Async} ausgeführt wird.

Mit dem Muster MutationObserver können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.

Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.

Synchrone IDisposable

Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable

...

@code {
    ...

    public void Dispose()
    {
        obj?.Dispose();
    }
}

Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.

Pages/CounterWithTimerDisposal1.razor:

@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer = new(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}

Hinweis

Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized/OnInitializedAsyncnullDispose erstellt wird.

Pages/CounterWithTimerDisposal2.razor:

@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer;

    protected override void OnInitialized()
    {
        timer = new Timer(1000);
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer?.Dispose();
}

Weitere Informationen finden Sie unter

Asynchrone IAsyncDisposable

Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IAsyncDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IAsyncDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable

...

@code {
    ...

    public async ValueTask DisposeAsync()
    {
        if (obj is not null)
        {
            await obj.DisposeAsync();
        }
    }
}

Weitere Informationen finden Sie unter

Zuweisung von null zu beseitigten Objekten

In der Regel ist es nicht notwendig, null freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null zugewiesen werden muss, gehören:

  • Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie null nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen.
  • Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von null das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.

Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null zuzuweisen. In den seltenen Fällen, in denen null einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null zugewiesen werden muss.

StateHasChanged

Hinweis

Der Aufruf von StateHasChanged in Dispose wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.

Ereignishandler

Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose-Methode:

  • Ansatz mit einem privatem Feld und Lambdaausdruck

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
        private EventHandler<FieldChangedEventArgs> fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new(model);
    
            fieldChanged = (_, __) =>
            {
                // ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • Ansatz mit einer privaten Methode

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
    
        protected override void OnInitialized()
        {
            editContext = new(model);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            // ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    

Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable und IAsyncDisposable.

Anonyme Funktionen, Methoden und Ausdrücke

Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.

  • Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):

    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        formInvalid = !editContext.Validate();
        StateHasChanged();
    }
    
    protected override void OnInitialized()
    {
        editContext = new(starship);
        editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
    }
    
  • Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):

    private ValidationMessageStore messageStore;
    
    [CascadingParameter]
    private EditContext CurrentEditContext { get; set; }
    
    protected override void OnInitialized()
    {
        ...
    
        messageStore = new(CurrentEditContext);
    
        CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
        CurrentEditContext.OnFieldChanged += (s, e) => 
            messageStore.Clear(e.FieldIdentifier);
    }
    

    Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.

Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose und DisposeAsync.

Abbrechbare Hintergrundarbeit

Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.

Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:

  • Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
  • Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
  • Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
  • Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
  • Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.

So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:

Im folgenden Beispiel:

  • await Task.Delay(5000, cts.Token); stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.
  • BackgroundResourceMethod stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn die Resource vor dem Aufruf der Methode verworfen wird.

Pages/BackgroundWork.razor:

@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger

<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>

@code {
    private Resource resource = new();
    private CancellationTokenSource cts = new();

    protected async Task LongRunningWork()
    {
        Logger.LogInformation("Long running work started");

        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod(Logger);
    }

    public void Dispose()
    {
        Logger.LogInformation("Executing Dispose");
        cts.Cancel();
        cts.Dispose();
        resource?.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
        {
            logger.LogInformation("BackgroundResourceMethod: Start method");

            if (disposed)
            {
                logger.LogInformation("BackgroundResourceMethod: Disposed");
                throw new ObjectDisposedException(nameof(Resource));
            }

            // Take action on the Resource

            logger.LogInformation("BackgroundResourceMethod: Action on Resource");
        }

        public void Dispose()
        {
            disposed = true;
        }
    }
}

Blazor Server-Ereignisse zur Wiederherstellung von Verbindungen

Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.

Die Razor-Komponente verarbeitet Razor-Ereignisse des Komponentenlebenszyklus in einer Reihe von synchronen und asynchronen Lebenszyklusmethoden. Die Lebenszyklusmethoden können außer Kraft gesetzt werden, um während der Komponenteninitialisierung und des Renderings zusätzliche Vorgänge mit Komponenten durchzuführen.

Lebenszyklusereignisse

Die folgenden vereinfachten Diagramme veranschaulichen die Verarbeitung von Lebenszyklusereignissen der Razor-Komponente. Die C#-Methoden, die den Lebenszyklusereignissen zugeordnet sind, werden anhand von Beispielen in den folgenden Abschnitten dieses Artikels definiert.

Ereignisse des Komponentenlebenszyklus:

  1. Wenn die Komponente erstmalig ein Rendering für eine Anforderung ausführt, führen Sie folgende Schritte durch:
    • Erstellen Sie die Instanz der Komponente.
    • Führen Sie eine Eigenschaftsinjektion durch. Führen Sie aus SetParametersAsync.
    • Rufen Sie OnInitialized{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  2. Rufen Sie OnParametersSet{Async} auf. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Hinweis

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor eine Komponente gerendert wird. Weitere Informationen finden Sie im Abschnitt „Behandeln unvollständiger asynchroner Aktionen beim Rendern“ weiter unten in diesem Artikel.

Eine übergeordnete Komponente wird vor den untergeordneten Komponenten gerendert, da das Rendern bestimmt, welche untergeordneten Elemente vorhanden sind. Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert zuerst abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Komponentenlebenszyklusereignisse einer Razor-Komponente in Blazor

Verarbeitung von DOM-Ereignissen (Document Object Model):

  1. Der Ereignishandler wird ausgeführt.
  2. Wenn ein unvollständiges Task-Element zurückgegeben wird, wird auf Task gewartet. Anschließend wird die Komponente erneut gerendert.
  3. Rendering für alle synchronen Arbeiten und vollständigen Task-Elemente.

Verarbeitung von DOM-Ereignissen (Document Object Model)

Der Render-Lebenszyklus:

  1. Vermeiden Sie weitere Renderingvorgänge für die Komponente in den folgenden Fällen:
  2. Erstellen Sie das Diff (Unterschied) der Renderstruktur, und rendern Sie die Komponente.
  3. Warten Sie, bis das DOM aktualisiert wurde.
  4. Rufen Sie OnAfterRender{Async} auf.

Lebenszyklus von Rendervorgängen

Wenn StateHasChanged von Entwicklern aufgerufen wird, führt dies zu einem Rendervorgang. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

In diesem Artikel werden einige Aspekte der Komponentenlebenszyklus-Ereignisverarbeitung vereinfacht, um komplexe Frameworklogik zu erklären. Möglicherweise müssen Sie auf die ComponentBase-Verweisquelle zugreifen, um die benutzerdefinierte Ereignisverarbeitung in die Lebenszyklusereignisverarbeitung von Blazor zu integrieren. Codekommentare in der Verweisquelle enthalten zusätzliche Hinweise zur Lebenszyklusereignisverarbeitung, die in diesem Artikel oder in der API-Dokumentation nicht enthalten sind. Beachten Sie, dass sich die Lebenszyklusereignisverarbeitung von Blazor im Laufe der Zeit geändert hat und ohne Ankündigung in jeder Version geändert werden kann.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Wenn Parameter festgelegt wurden (SetParametersAsync)

SetParametersAsync legt Parameter fest, die vom übergeordneten Element der Komponente in der Renderstruktur oder aus Routingparametern bereitgestellt werden.

Der Parameter ParameterView der Methode enthält bei jedem Aufruf von SetParametersAsync den Satz von Komponentenparameterwerten für die Komponente. Durch Überschreiben dieser SetParametersAsync-Methode kann Entwicklercode direkt mit den Parametern von ParameterView interagieren.

Die Standardimplementierung von SetParametersAsync legt den Wert der einzelnen Eigenschaften mit dem [Parameter]- oder [CascadingParameter]-Attribut fest, die einen entsprechenden Wert in ParameterView aufweisen. Parameter, die keinen entsprechenden Wert in ParameterView haben, bleiben unverändert.

Wenn base.SetParametersAsync nicht aufgerufen wird, kann der Entwicklercode den eingehenden Parameterwert in jeder gewünschten Weise interpretieren. Beispielsweise ist es nicht erforderlich, die eingehenden Parameter den Eigenschaften der Klasse zuzuordnen.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Im folgenden Beispiel weist ParameterView.TryGetValue den Wert des Param-Parameters value zu, wenn die Analyse eines Routingparameters für Param erfolgreich ist. Wenn value nicht null lautet, wird der Wert von der Komponente angezeigt.

Beim Abgleich von Routenparametern wird die Groß-/Kleinschreibung zwar nicht beachtet, aber in der Routenvorlage stimmt nur TryGetValue mit Parameternamen überein, bei denen die Groß-/Kleinschreibung beachtet wird. Das folgende Beispiel erfordert die Verwendung von /{Param?} in der Routenvorlage, um den Wert mit TryGetValue und nicht mit /{param?} abzurufen. Wenn /{param?} in diesem Szenario verwendet wird, gibt TryGetValuefalse zurück, und message wird auf keine der message-Zeichenfolgen festgelegt.

Pages/SetParamsAsync.razor:

@page "/set-params-async"
@page "/set-params-async/{Param}"

<p>@message</p>

@code {
    private string message = "Not set";

    [Parameter]
    public string Param { get; set; }

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        if (parameters.TryGetValue<string>(nameof(Param), out var value))
        {
            if (value is null)
            {
                message = "The value of 'Param' is null.";
            }
            else
            {
                message = $"The value of 'Param' is {value}.";
            }
        }

        await base.SetParametersAsync(parameters);
    }
}

Komponenteninitialisierung (OnInitialized{Async})

OnInitialized und OnInitializedAsync werden aufgerufen, wenn die Komponente initialisiert wird, nachdem sie ihre anfänglichen Parameter in SetParametersAsync erhalten hat.

Wenn die synchrone Initialisierung der übergeordneten Komponente verwendet wird, wird die übergeordnete Initialisierung garantiert vor der Initialisierung der untergeordneten Komponente abgeschlossen. Wenn die asynchrone Initialisierung der übergeordneten Komponente verwendet wird, kann die Abschlussreihenfolge der Initialisierung der übergeordneten und untergeordneten Komponente nicht bestimmt werden, da sie vom ausgeführten Initialisierungscode abhängt.

Für einen synchronen Betrieb setzen Sie OnInitialized außer Kraft:

Pages/OnInit.razor:

@page "/on-init"

<p>@message</p>

@code {
    private string message;

    protected override void OnInitialized()
    {
        message = $"Initialized at {DateTime.Now}";
    }
}

Überschreiben Sie OnInitializedAsync, und verwenden Sie den Operator await, um einen asynchronen Vorgang durchzuführen:

protected override async Task OnInitializedAsync()
{
    await ...
}

Blazor-Apps, die ihren Inhalt auf dem Server vorab rendern, rufen OnInitializedAsynczweimal auf:

  • Einmal, wenn die Komponente anfänglich statisch als Teil der Seite gerendert wird.
  • Ein zweites Mal, wenn der Browser die Komponente rendert.

Informationen zum Verhindern, dass Entwicklercode beim Vorabrendern in OnInitializedAsync zwei Mal ausgeführt wird, finden Sie im Abschnitt Zustandsbehaftete erneute Verbindung nach Vorabrendern. Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für Vorabrendern in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Während eine Blazor-App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS-interop). Komponenten müssen wahrscheinlich unterschiedlich rendern, wenn dafür ein Prerendering durchgeführt wurde. Weitere Informationen finden Sie im Abschnitt Voarabrendering mit JavaScript-Interop.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Nachdem Parameter festgelegt wurden (OnParametersSet{Async})

OnParametersSet oder OnParametersSetAsync wird aufgerufen:

  • Nachdem die Komponente in OnInitialized oder OnInitializedAsync initialisiert wurde.

  • Wenn die übergeordnete Komponente erneut gerendert wird und Folgendes bereitstellt:

    • Bekannte oder einfache unveränderliche Typen, wenn sich mindestens ein Parameter geändert hat.
    • Parameter mit komplexem Typ. Das Framework kann nicht wissen, ob die Werte eines Parameters mit komplexem Typ intern mutiert sind, daher behandelt das Framework den Parametersatz immer als geändert, wenn mindestens ein Parameter mit komplexem Typ vorhanden ist.

    Weitere Informationen über Renderingkonventionen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Navigieren Sie für die folgende Beispielkomponente unter einer URL zu der Seite der Komponente:

  • Mit einem Startdatum, das von StartDate empfangen wird: /on-parameters-set/2021-03-19
  • Ohne Startdatum, wobei StartDate einem Wert der aktuellen Ortszeit zugewiesen wird: /on-parameters-set

Pages/OnParamsSet.razor:

@page "/on-params-set"
@page "/on-params-set/{StartDate:datetime}"

<p>@message</p>

@code {
    private string message;

    [Parameter]
    public DateTime StartDate { get; set; }

    protected override void OnParametersSet()
    {
        if (StartDate == default)
        {
            StartDate = DateTime.Now;

            message = $"No start date in URL. Default value applied (StartDate: {StartDate}).";
        }
        else
        {
            message = $"The start date in the URL was used (StartDate: {StartDate}).";
        }
    }
}

Asynchrones Arbeiten bei der Anwendung von Parametern und Eigenschaftswerten muss während des OnParametersSetAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnParametersSetAsync()
{
    await ...
}

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Weitere Informationen zu Routenparametern und Einschränkungen finden Sie unter ASP.NET Core ASP.NET Core: Routing und Navigation in Blazor.

Nach dem Rendern der Komponente (OnAfterRender{Async})

OnAfterRender und OnAfterRenderAsync werden aufgerufen, nachdem eine Komponente das Rendering beendet hat. Element- und Komponentenverweise werden an dieser Stelle aufgefüllt. Verwenden Sie diese Stufe, um zusätzliche Initialisierungsschritte mit dem gerenderten Inhalt durchzuführen, z. B. JS-Interop-Aufrufe, die mit den gerenderten DOM-Elementen interagieren.

Der Parameter firstRender für OnAfterRender und OnAfterRenderAsync:

  • Wird auf true festgelegt, wenn die Komponenteninstanz zum ersten Mal gerendert wird.
  • Kann verwendet werden, um sicherzustellen, dass die Initialisierung nur einmal durchgeführt wird.

Pages/AfterRender.razor:

@page "/after-render"
@using Microsoft.Extensions.Logging
@inject ILogger<AfterRender> Logger 

<button @onclick="LogInformation">Log information (and trigger a render)</button>

@code {
    private string message = "Initial assigned message.";

    protected override void OnAfterRender(bool firstRender)
    {
        Logger.LogInformation("OnAfterRender(1): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);

        if (firstRender)
        {
            message = "Executed for the first render.";
        }
        else
        {
            message = "Executed after the first render.";
        }

        Logger.LogInformation("OnAfterRender(2): firstRender: " +
            "{FirstRender}, message: {Message}", firstRender, message);
    }

    private void LogInformation()
    {
        Logger.LogInformation("LogInformation called");
    }
}

Asynchrones Arbeiten unmittelbar nach dem Rendering muss während des OnAfterRenderAsync-Lebenszyklusereignisses erfolgen:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

Selbst wenn Sie ein Task von OnAfterRenderAsync zurückgeben, plant das Framework keinen weiteren Renderzyklus für Ihre Komponente ein, sobald diese Aufgabe abgeschlossen ist. Damit soll eine unendliche Renderschleife vermieden werden. Dies unterscheidet sich von den anderen Lebenszyklusmethoden, die einen weiteren Renderzyklus planen, sobald ein zurückgegebener Task abgeschlossen ist.

OnAfterRender und OnAfterRenderAsyncwerden beim Prerendering auf dem Server nicht aufgerufen. Die Methoden werden aufgerufen, wenn die Komponente nach dem Vorabrendering gerendert und interaktiv ist. Beim Vorabrendern der App:

  1. Die Komponente wird auf dem Server ausgeführt, um in der HTTP-Antwort statisches HTML-Markup zu generieren. Während dieser Phase werden OnAfterRender und OnAfterRenderAsync nicht aufgerufen.
  2. Wenn das Blazor-Skript (blazor.server.js oder blazor.webassembly.js) im Browser gestartet wird, wird die Komponente in einem interaktiven Renderingmodus neu gestartet. Nachdem eine Komponente neu gestartet wurde, werdenOnAfterRender und OnAfterRenderAsync aufgerufen, da sich die App nicht mehr in der Phase des Prerenderings befindet.

Wenn Ereignishandler im Entwicklercode bereitgestellt werden, wenden Sie Unhook bei der Bereinigung an. Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable IAsyncDisposableIDisposableIAsyncDisposable.

Statusänderungen (StateHasChanged)

StateHasChanged benachrichtigt die Komponente, dass sich ihr Zustand geändert hat. Gegebenenfalls bewirkt der Aufruf von StateHasChanged das erneute Rendern der Komponente.

StateHasChanged wird für EventCallback-Methoden automatisch aufgerufen. Weitere Informationen zu Ereignisrückrufen finden Sie unter Blazor-Ereignisbehandlung in ASP.NET Core.

Weitere Informationen zum Rendern von Komponenten und zum Aufrufen von StateHasChanged, einschließlich des Aufrufs mit ComponentBase.InvokeAsync, finden Sie unter Razor-Komponentenrendering in ASP.NET Core.

Behandeln unvollständiger asynchroner Aktionen beim Rendern

Asynchrone Aktionen, die in Lebenszyklusereignissen ausgeführt werden, sind möglicherweise nicht abgeschlossen, bevor die Komponente gerendert wird. Objekte können während der Ausführung der Lebenszyklusmethode null oder unvollständig mit Daten gefüllt sein. Stellen Sie eine Renderinglogik bereit, um zu bestätigen, dass die Objekte initialisiert sind. Rendern Sie UI-Elemente für Platzhalter (z. B. eine Nachricht zum Ladevorgang), während Objekte null sind.

In der FetchData-Komponente der Blazor-Vorlagen wird OnInitializedAsync außer Kraft gesetzt, um Vorhersagedaten asynchron abzurufen (forecasts). Wenn forecasts gleich null ist, wird dem Benutzer eine Nachricht zum Ladevorgang angezeigt. Nachdem die von OnInitializedAsync zurückgegebene Task abgeschlossen ist, wird die Komponente mit dem aktualisierten Zustand neu gerendert.

Pages/FetchData.razor in der Blazor Server-Vorlage:

@page "/fetchdata"
@using BlazorSample.Data
@inject WeatherForecastService ForecastService

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <!-- forecast data in table element content -->
    </table>
}

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Behandeln von Fehlern

Informationen zum Behandeln von Fehlern während der Ausführung der Lebenszyklusmethode finden Sie unter Behandeln von Fehlern Blazor-Apps in ASP.NET Core.

Zustandsbehaftete erneute Verbindung nach dem Prerendering

Wenn in einer Blazor Server-App RenderMode gleich ServerPrerendered ist, wird die Komponente zunächst statisch als Teil der Seite gerendert. Sobald der Browser eine SignalR-Verbindung mit dem Server herstellt, wird die Komponente erneut gerendert und ist nun interaktiv. Wenn die OnInitialized{Async}-Lebenszyklusmethode zur Initialisierung der Komponente vorhanden ist, wird die Methode zweimal ausgeführt:

  • Wenn die Komponente statisch vorab gerendert ist.
  • Nachdem die Serververbindung hergestellt wurde.

Dies kann zu einer spürbaren Änderung der auf der Benutzeroberfläche angezeigten Daten führen, wenn die Komponente schließlich gerendert wird. Um dieses doppelte Renderingverhalten in einer Blazor Server-App zu vermeiden, übergeben Sie einen Bezeichner, um den Status während des Vorabrenderns zwischenzuspeichern und den Status nach dem Vorabrendern abzurufen.

Der folgende Code veranschaulicht ein aktualisiertes WeatherForecastService-Element in einer vorlagenbasierten Blazor Server-App, die das doppelte Rendering vermeidet. Im folgenden Beispiel simuliert das erwartete Delay-Element (await Task.Delay(...)) eine kurze Verzögerung, bevor Daten aus der GetForecastAsync-Methode zurückgegeben werden.

WeatherForecastService.cs:

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using BlazorSample.Shared;

public class WeatherForecastService
{
    private static readonly string[] summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public WeatherForecastService(IMemoryCache memoryCache)
    {
        MemoryCache = memoryCache;
    }

    public IMemoryCache MemoryCache { get; }

    public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
    {
        return MemoryCache.GetOrCreateAsync(startDate, async e =>
        {
            e.SetOptions(new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow =
                    TimeSpan.FromSeconds(30)
            });

            var rng = new Random();

            await Task.Delay(TimeSpan.FromSeconds(10));

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = summaries[rng.Next(summaries.Length)]
            }).ToArray();
        });
    }
}

Weitere Informationen zu finden Sie RenderModeunter Leitfaden zu BlazorSignalR in ASP.NET Core.

Obwohl sich der Inhalt dieses Abschnitts auf Blazor Server und zustandsbehaftete erneute SignalR-Verbindung konzentriert, umfasst das Szenario für ein Prerendering in gehosteten Blazor WebAssembly-Apps (WebAssemblyPrerendered) ähnliche Bedingungen und Verfahren, um die doppelte Ausführung von Entwicklercode zu verhindern. Informationen zum Beibehalten des Zustands während der Ausführung von Initialisierungscode während des Prerenderings finden Sie unter Prerendering und Integrieren von Razor-Komponenten in ASP.NET Core.

Voarabrendering mit JavaScript-Interop

Dieser Abschnitt gilt für Blazor Server und gehostete Blazor WebAssembly-Apps, die Razor-Komponenten vorab rendern. Informationen zum Prerendering finden Sie in Prerendering und Integrieren von -Komponenten in ASP.NET Core.

Während eine App vorab gerendert wird, sind bestimmte Aktionen nicht möglich, z. B. Aufrufe in JavaScript (JS).

Im folgenden Beispiel wird die Funktion setElementText1 im Element <head> platziert. Die Funktion wird mit JSRuntimeExtensions.InvokeVoidAsync aufgerufen und gibt keinen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText1 = (element, text) => element.innerText = text;
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Das OnAfterRender{Async}-Lebenszyklusereignis wird beim Voarabrendering auf dem Server nicht aufgerufen. Überschreiben Sie die OnAfterRender{Async}-Methode, um JS-Interopaufrufe zu verzögern, bis die Komponente nach dem Vorabendering gerendert und auf dem Client interaktiv ist.

Pages/PrerenderedInterop1.razor:

@page "/prerendered-interop-1"
@using Microsoft.JSInterop
@inject IJSRuntime JS

<div @ref="divElement">Text during render</div>

@code {
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync(
                "setElementText1", divElement, "Text after render");
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText1 = (element, text) => element.innerText = text;

Die folgende Komponente veranschaulicht, wie JS-Interop als Teil der Initialisierungslogik einer Komponente auf eine Weise verwendet werden kann, die mit dem Prerendering kompatibel ist. Die Komponente zeigt, dass es möglich ist, in OnAfterRenderAsync ein Renderingupdate zu initiieren. Der Entwickler muss in diesem Szenario unbedingt vermeiden, eine Endlosschleife zu erstellen.

Im folgenden Beispiel wird die Funktion setElementText2 im Element <head> platziert. Die Funktion wird mit IJSRuntime.InvokeAsync aufgerufen und gibt einen Wert zurück.

Hinweis

Allgemeine Anleitungen zum Speicherort von JS und unsere Empfehlungen für Produktions-Apps finden Sie unter ASP.NET Core Blazor JavaScript-Interoperabilität (JS-Interop).

<script>
  window.setElementText2 = (element, text) => {
    element.innerText = text;
    return text;
  };
</script>

Warnung

Im vorangehenden Beispiel wird das Dokumentobjektmodell (DOM) direkt zu Demonstrationszwecken geändert. Das direkte Ändern des DOM mit JS wird in den meisten Szenarios nicht empfohlen, da JS die Änderungsnachverfolgung von Blazor beeinträchtigen kann. Weitere Informationen finden Sie unter JavaScript-Interoperabilität von in ASP.NET Core (JS-Interoperabilität).

Wenn JSRuntime.InvokeAsync aufgerufen wird, wird ElementReference nur in OnAfterRenderAsync und nicht in einer früheren Lebenszyklusmethode verwendet, da es kein JS-Element gibt, bis die Komponente gerendert wird.

StateHasChanged wird aufgerufen, um die Komponente mit dem neuen Zustand, der vom JS-Interop-Aufruf abgerufen wurde, erneut zu rendern. Weitere Informationen erhalten Sie unter Razor-Komponentenrendering in ASP.NET Core. Der Code erstellt keine Endlosschleife, da StateHasChanged nur aufgerufen wird, wenn datanull ist.

Pages/PrerenderedInterop2.razor:

@page "/prerendered-interop-2"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS

<p>
    Get value via JS interop call:
    <strong id="val-get-by-interop">@(data ?? "No value yet")</strong>
</p>

<p>
    Set value via JS interop call:
</p>

<div id="val-set-by-interop" @ref="divElement"></div>

@code {
    private string? data;
    private ElementReference divElement;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && data == null)
        {
            data = await JS.InvokeAsync<string>(
                "setElementText2", divElement, "Hello from interop call!");

            StateHasChanged();
        }
    }
}

Hinweis

Das Beispiel oben „verschmutzt“ den Client mit globalen Methoden. Einen besseren Ansatz für Produktions-Apps finden Sie unter JavaScript-Isolation in JavaScript-Modulen.

Beispiel:

export setElementText2 = (element, text) => {
  element.innerText = text;
  return text;
};

Beseitigung von Komponenten mit IDisposable und IAsyncDisposable

Wenn eine Komponente IDisposable und/oder IAsyncDisposable implementiert, fordert das Framework, dass nicht verwaltete Ressourcen beseitigt werden, wenn die Komponente aus der Benutzeroberfläche entfernt wird. Das Entfernen kann jederzeit erfolgen, auch während der Initialisierung von Komponenten.

Komponenten sollten IDisposable und IAsyncDisposable nicht gleichzeitig implementieren müssen. Wenn beide implementiert werden, führt das Framework nur die asynchrone Überladung aus.

Entwicklercode muss so gestaltet sein, dass IAsyncDisposable-Implementierungen nicht viel Zeit in Anspruch nehmen.

DOM-Bereinigungsvorgänge (Document Object Model, Dokumentobjektmodell) während der Beseitigung von Komponenten

Führen Sie nicht den JS-Interopcode für die DOM-Bereinigungsvorgänge während der Beseitigung von Komponenten aus. Verwenden Sie stattdessen aus folgenden Gründen das MutationObserver-Muster in JavaScript auf dem Client:

  • Die Komponente wurde möglicherweise nach Ausführung des Bereinigungscodes in Dispose{Async} aus dem DOM entfernt.
  • In einer Blazor Server-App wurde der Blazor-Renderer möglicherweise durch das Framework verworfen, wenn der Bereinigungscode in Dispose{Async} ausgeführt wird.

Mit dem Muster MutationObserver können Sie eine Funktion ausführen, wenn ein Element aus dem DOM entfernt wird.

Anleitungen zu JSDisconnectedException in Blazor Server-Apps, wenn eine Verbindung getrennt wird, finden Sie unter Aufrufen von JavaScript-Funktionen aus .NET-Methoden in ASP.NET Core Blazor oder Aufrufen von .NET-Methoden aus JavaScript-Funktionen in ASP.NET Core Blazor. Allgemeine Anleitungen zur Behandlung von JavaScript-Interopfehlern finden Sie im Abschnitt JavaScript-Interop unter Behandeln von Fehlern in ASP.NET Core Blazor-Apps.

Synchrone IDisposable

Verwenden Sie für synchrone Beseitigungsaufgaben IDisposable.Dispose.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IDisposable

...

@code {
    ...

    public void Dispose()
    {
        obj?.Dispose();
    }
}

Wenn ein einzelnes Objekt beseitigt werden muss, kann ein Lambdaausdruck verwendet werden, um das Objekt zu beseitigen, wenn Dispose aufgerufen wird. Das folgende Beispiel wird im Artikel Razor-Komponentenrendering in ASP.NET Core angezeigt und veranschaulicht die Verwendung eines Lambdaausdrucks zur Bereinigung eines Timer-Elements.

Pages/CounterWithTimerDisposal1.razor:

@page "/counter-with-timer-disposal-1"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer = new Timer(1000);

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();
}

Hinweis

Im vorangegangenen Beispiel wird der Aufruf von StateHasChanged von einem Aufruf von ComponentBase.InvokeAsync umschlossen, da der Rückruf außerhalb des Synchronisierungskontexts von Blazor aufgerufen wird. Weitere Informationen finden Sie unter Rendering von Razor-Komponenten in ASP.NET Core.

Überprüfen Sie , bevor aufgerufen wird, wenn das Objekt in einer Lebenszyklusmethode wie OnInitialized/OnInitializedAsyncnullDispose erstellt wird.

Pages/CounterWithTimerDisposal2.razor:

@page "/counter-with-timer-disposal-2"
@using System.Timers
@implements IDisposable

<h1>Counter with <code>Timer</code> disposal</h1>

<p>Current count: @currentCount</p>

@code {
    private int currentCount = 0;
    private Timer timer;

    protected override void OnInitialized()
    {
        timer = new Timer(1000);
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            currentCount++;
            StateHasChanged();
        });
    }

    public void Dispose() => timer?.Dispose();
}

Weitere Informationen finden Sie unter

Asynchrone IAsyncDisposable

Verwenden Sie für asynchrone Beseitigungsaufgaben IAsyncDisposable.DisposeAsync.

Die folgende Komponente führt folgende Aktionen aus:

  • Implementiert IAsyncDisposable mit der @implementsRazor-Direktive.
  • Beseitigt obj (ein nicht verwalteter Typ, der IAsyncDisposable implementiert).
  • Eine NULL-Überprüfung wird ausgeführt, da obj in einer Lebenszyklusmethode erstellt wird (nicht gezeigt).
@implements IAsyncDisposable

...

@code {
    ...

    public async ValueTask DisposeAsync()
    {
        if (obj is not null)
        {
            await obj.DisposeAsync();
        }
    }
}

Weitere Informationen finden Sie unter

Zuweisung von null zu beseitigten Objekten

In der Regel ist es nicht notwendig, null freigegebenen Objekten zuzuweisen, nachdem Dispose/DisposeAsync aufgerufen wurde. Zu den seltenen Fällen, in denen null zugewiesen werden muss, gehören:

  • Wenn der Typ des Objekts mangelhaft implementiert ist und wiederholte Aufrufe von Dispose/DisposeAsync nicht toleriert werden, müssen Sie null nach der Beseitigung zuweisen, um weitere Aufrufe von Dispose/DisposeAsync kontrolliert zu überspringen.
  • Wenn ein langlebiger Prozess weiterhin einen Verweis auf ein beseitigtes Objekt enthält, kann der Garbage Collector durch Zuweisung von null das Objekt trotz des langlebigen Prozesses, der einen Verweis auf das Objekt enthält, freigeben.

Dies sind ungewöhnliche Szenarien. Für Objekte, die ordnungsgemäß implementiert sind und sich normal verhalten, macht es keinen Sinn, beseitigten Objekten null zuzuweisen. In den seltenen Fällen, in denen null einem Objekt zugewiesen werden muss, wird empfohlen, den entsprechenden Grund zu dokumentieren und nach einer Lösung zu suchen, die vermeidet, dass null zugewiesen werden muss.

StateHasChanged

Hinweis

Der Aufruf von StateHasChanged in Dispose wird nicht unterstützt. StateHasChanged könnte im Rahmen des Beendens des Renderers aufgerufen werden, sodass die Anforderung von UI-Updates an diesem Punkt nicht unterstützt wird.

Ereignishandler

Kündigen Sie die .NET-Ereignisabonnements der Ereignishandler immer. Die folgenden Blazor-Formularbeispiele veranschaulichen die Abbestellung eines Ereignishandlers in der Dispose-Methode:

  • Ansatz mit einem privatem Feld und Lambdaausdruck

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
        private EventHandler<FieldChangedEventArgs> fieldChanged;
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(model);
    
            fieldChanged = (_, __) =>
            {
                // ...
            };
    
            editContext.OnFieldChanged += fieldChanged;
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= fieldChanged;
        }
    }
    
  • Ansatz mit einer privaten Methode

    @implements IDisposable
    
    <EditForm EditContext="@editContext">
        ...
        <button type="submit" disabled="@formInvalid">Submit</button>
    </EditForm>
    
    @code {
        // ...
    
        protected override void OnInitialized()
        {
            editContext = new EditContext(model);
            editContext.OnFieldChanged += HandleFieldChanged;
        }
    
        private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
        {
            // ...
        }
    
        public void Dispose()
        {
            editContext.OnFieldChanged -= HandleFieldChanged;
        }
    }
    

Weitere Informationen finden Sie im Abschnitt Beseitigung von Komponenten mit IDisposable und IAsyncDisposable.

Anonyme Funktionen, Methoden und Ausdrücke

Wenn anonyme Funktionen, Methoden oder Ausdrücke verwendet werden, ist es nicht erforderlich, IDisposable zu implementieren und Delegaten abzubestellen. Es kann jedoch problematisch sein, einen Delegaten abzubestellen, wenn das Objekt, das das Ereignis offenlegt, die Lebensdauer der Komponente überschreitet, die den Delegaten registriert. Wenn dies der Fall ist, führt dies zu einem Arbeitsspeicherverlust, da der registrierte Delegat das ursprüngliche Objekt aufrecht erhält. Verwenden Sie daher nur die folgenden Ansätze, wenn Sie wissen, dass der Ereignisdelegat schnell beseitigt wird. Wenn Sie sich bezüglich der Lebensdauer von Objekten, die eine Bereinigung erfordern, nicht sicher sind, abonnieren Sie eine Delegatmethode, und verwerfen Sie den Delegaten wie in den früheren Beispielen ordnungsgemäß.

  • Anonymer Lambdamethodenansatz (explizite Bereinigung nicht erforderlich):

    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        formInvalid = !editContext.Validate();
        StateHasChanged();
    }
    
    protected override void OnInitialized()
    {
        editContext = new EditContext(starship);
        editContext.OnFieldChanged += (s, e) => HandleFieldChanged((editContext)s, e);
    }
    
  • Anonymer Lambdaausdrucksansatz (explizite Bereinigung nicht erforderlich):

    private ValidationMessageStore messageStore;
    
    [CascadingParameter]
    private EditContext CurrentEditContext { get; set; }
    
    protected override void OnInitialized()
    {
        ...
    
        messageStore = new ValidationMessageStore(CurrentEditContext);
    
        CurrentEditContext.OnValidationRequested += (s, e) => messageStore.Clear();
        CurrentEditContext.OnFieldChanged += (s, e) => 
            messageStore.Clear(e.FieldIdentifier);
    }
    

    Das vollständige Beispiel mit dem obigen Code mit anonymen Lambdaausdrücken finden Sie im Artikel ASP.NET Core Blazor-Formulare und -Eingabekomponenten.

Weitere Informationen finden Sie unter Bereinigen von nicht verwalteten Ressourcen und den Themen zum Implementieren der Methoden Dispose und DisposeAsync.

Abbrechbare Hintergrundarbeit

Komponenten führen häufig Hintergrundaufgaben aus, die lange dauern, zum Beispiel die Durchführung von Netzwerkaufrufen (HttpClient) und die Interaktion mit Datenbanken. Es ist wünschenswert, die Hintergrundarbeit zu unterbinden, um Systemressourcen in mehreren Situationen zu sparen. Beispielsweise werden asynchrone Hintergrundvorgänge nicht automatisch beendet, wenn ein Benutzer von einer Komponente wegnavigiert.

Andere Gründe, warum Arbeitselemente, die im Hintergrund ausgeführt werden, unterbrochen werden müssen, sind die folgenden:

  • Eine ausgeführte Hintergrundaufgabe wurde mit fehlerhaften Eingabedaten oder Verarbeitungsparametern gestartet.
  • Die aktuellen Arbeitselemente, die im Hintergrund ausgeführt werden, müssen durch neue Arbeitselemente ersetzt werden.
  • Die Priorität der aktuell ausgeführten Aufgaben muss geändert werden.
  • Die App muss für die erneute Serverbereitstellung heruntergefahren werden.
  • Serverressourcen werden eingeschränkt und erfordern die Neuplanung von Arbeitselementen, die im Hintergrund ausgeführt werden.

So implementieren Sie ein abbrechbares Hintergrundarbeitsmuster in einer Komponente:

Im folgenden Beispiel:

  • await Task.Delay(5000, cts.Token); stellt asynchrone Hintergrundaufgaben mit langer Ausführungszeit dar.
  • BackgroundResourceMethod stellt eine Hintergrundmethode mit langer Ausführungszeit dar, die nicht gestartet werden sollte, wenn die Resource vor dem Aufruf der Methode verworfen wird.

Pages/BackgroundWork.razor:

@page "/background-work"
@using System.Threading
@using Microsoft.Extensions.Logging
@implements IDisposable
@inject ILogger<BackgroundWork> Logger

<button @onclick="LongRunningWork">Trigger long running work</button>
<button @onclick="Dispose">Trigger Disposal</button>

@code {
    private Resource resource = new Resource();
    private CancellationTokenSource cts = new CancellationTokenSource();

    protected async Task LongRunningWork()
    {
        Logger.LogInformation("Long running work started");

        await Task.Delay(5000, cts.Token);

        cts.Token.ThrowIfCancellationRequested();
        resource.BackgroundResourceMethod(Logger);
    }

    public void Dispose()
    {
        Logger.LogInformation("Executing Dispose");
        cts.Cancel();
        cts.Dispose();
        resource?.Dispose();
    }

    private class Resource : IDisposable
    {
        private bool disposed;

        public void BackgroundResourceMethod(ILogger<BackgroundWork> logger)
        {
            logger.LogInformation("BackgroundResourceMethod: Start method");

            if (disposed)
            {
                logger.LogInformation("BackgroundResourceMethod: Disposed");
                throw new ObjectDisposedException(nameof(Resource));
            }

            // Take action on the Resource

            logger.LogInformation("BackgroundResourceMethod: Action on Resource");
        }

        public void Dispose()
        {
            disposed = true;
        }
    }
}

Blazor Server-Ereignisse zur Wiederherstellung von Verbindungen

Die in diesem Artikel behandelten Ereignisse zum Komponentenlebenszyklus werden getrennt von den Blazor Server-Ereignishandlern zur Wiederherstellung von Verbindungen ausgeführt. Wenn bei einer Blazor Server-App die SignalR-Verbindung mit dem Client getrennt wird, werden nur Updates der Benutzeroberfläche unterbrochen. Updates der Benutzeroberfläche werden fortgesetzt, sobald die Verbindung wiederhergestellt wurde. Weitere Informationen zu Verbindungshandlerereignissen und zur Konfiguration finden Sie unter Leitfaden zu BlazorSignalR in ASP.NET Core.