Aufrufen von .NET-Methoden von JavaScript-Funktionen in ASP.NET Core Blazor

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird erläutert, wie .NET-Methoden aus JavaScript (JS) aufgerufen werden.

Weitere Informationen zum Aufrufen von JS-Funktionen in .NET finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core.

Aufrufen einer statischen .NET-Methode

Um eine statische .NET-Methode über JavaScript (JS) aufzurufen, verwenden Sie die JS-Funktionen:

  • DotNet.invokeMethodAsync (empfohlen): Asynchron für serverseitige und clientseitige Komponenten.
  • DotNet.invokeMethod: Nur synchron für clientseitige Komponenten.

Übergeben Sie den Namen der Assembly, die die Methode enthält, den Bezeichner der statischen .NET-Methode und alle Argumente.

Im folgenden Beispiel:

  • Der Platzhalter {ASSEMBLY NAME} ist der Assemblyname der App.
  • Der Platzhalter {.NET METHOD ID} ist der Bezeichner der .NET-Methode.
  • Der Platzhalter {ARGUMENTS} steht für optionale, durch Komma getrennte Argumente, die an die Methode übergeben werden sollen, von denen jedes JSON-serialisierbar sein muss.
DotNet.invokeMethodAsync('{ASSEMBLY NAME}', '{.NET METHOD ID}', {ARGUMENTS});

DotNet.invokeMethodAsync gibt ein JS Promise zurück, das das Ergebnis des Vorgangs darstellt. DotNet.invokeMethod (clientseitige Komponenten) gibt das Ergebnis des Vorgangs zurück.

Wichtig

Für serverseitige Komponenten wird die asynchrone Funktion (invokeMethodAsync) anstelle der synchronen Version (invokeMethod) empfohlen.

Die .NET-Methode muss öffentlich und statisch sein sowie das [JSInvokable]-Attribut aufweisen.

Im folgenden Beispiel:

  • Der Platzhalter {<T>} gibt den Rückgabetyp an, der nur für Methoden erforderlich ist, die einen Wert zurückgeben.
  • Der Platzhalter {.NET METHOD ID} ist der Bezeichner der Methode.
@code {
    [JSInvokable]
    public static Task{<T>} {.NET METHOD ID}()
    {
        ...
    }
}

Hinweis

Das Aufrufen von offenen generischen Methoden wird nicht mit statischen .NET-Methoden, sondern mit Instanzmethoden unterstützt. Weitere Informationen finden Sie im Abschnitt Aufrufen generischer .NET-Klassenmethoden.

In der folgenden Komponente gibt die C#-Methode ReturnArrayAsync ein int-Array zurück. Das [JSInvokable]-Attribut wird auf die Methode angewendet, sodass die Methode von JS aufgerufen werden kann.

CallDotnet1.razor:

@page "/call-dotnet-1"

<PageTitle>Call .NET 1</PageTitle>

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

<p>
    See the result in the developer tools console.
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

CallDotNetExample1.razor:

@page "/call-dotnet-example-1"

<h1>Call .NET Example 1</h1>

<p>
    <button onclick="returnArrayAsync()">
        Trigger .NET static method
    </button>
</p>

@code {
    [JSInvokable]
    public static Task<int[]> ReturnArrayAsync()
    {
        return Task.FromResult(new int[] { 1, 2, 3 });
    }
}

Das HTML-Attribut onclick des <button>-Elements ist die Ereignishandlerzuweisung onclick von JavaScript für die Verarbeitung von click-Ereignissen, nicht das @onclick-Direktivenattribut von Blazor. Die JS-Funktion returnArrayAsync wird als Handler zugewiesen.

Die folgende returnArrayAsync-JS-Funktion ruft die .NET-Methode ReturnArrayAsync der vorherigen Komponente auf und protokolliert das Ergebnis in der Webentwicklertools-Konsole des Browsers. BlazorSample ist der Assemblyname der App.

<script>
  window.returnArrayAsync = () => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync')
      .then(data => {
        console.log(data);
      });
    };
</script>

Hinweis

Allgemeine Anleitungen zu JS Standort und unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET Core Blazor -Apps.

Bei Auswahl der Trigger .NET static method -Schaltfläche werden die Arraydaten in der Konsolenausgabe der Entwicklertools des Browsers angezeigt. Das Format der Ausgabe unterscheidet sich von Browser zu Browser geringfügig. Die folgende Ausgabe veranschaulicht das Format von Microsoft Edge:

Array(3) [ 1, 2, 3 ]

Übergeben Sie Daten an eine .NET-Methode, wenn Sie die Funktion invokeMethodAsync aufrufen, indem Sie die Daten als Argumente übergeben.

Um die Übergabe von Daten an .NET zu veranschaulichen, sorgen Sie dafür, dass die vorangehende returnArrayAsyncJS-Funktion eine Startposition erhält, wenn die Funktion aufgerufen wird, und übergeben Sie den Wert als Argument an die Funktion invokeMethodAsync:

<script>
  window.returnArrayAsync = (startPosition) => {
    DotNet.invokeMethodAsync('BlazorSample', 'ReturnArrayAsync', startPosition)
      .then(data => {
        console.log(data);
      });
    };
</script>

Ändern Sie in der Komponente den Funktionsaufruf so, dass er eine Startposition enthält. Im folgenden Beispiel wird ein Wert von 5verwendet:

<button onclick="returnArrayAsync(5)">
    ...
</button>

Die aufrufbare ReturnArrayAsync-Methode der Komponente erhält die Startposition und konstruiert daraus das Array. Das Array wird für die Protokollierung an die Konsole zurückgegeben:

[JSInvokable]
public static Task<int[]> ReturnArrayAsync(int startPosition)
{
    return Task.FromResult(Enumerable.Range(startPosition, 3).ToArray());
}

Nachdem die App neu kompiliert und der Browser aktualisiert wurde, wird die folgende Ausgabe in der Konsole des Browsers angezeigt, wenn die Schaltfläche ausgewählt wird:

Array(3) [ 5, 6, 7 ]

Standardmäßig ist der .NET-Methodenbezeichner für den JS-Aufruf der .NET-Methodenname. Sie können aber mit dem [JSInvokable]-Attributkonstruktor einen anderen Bezeichner angeben. Im folgenden Beispiel ist DifferentMethodName der zugewiesene Methodenbezeichner für die ReturnArrayAsync-Methode:

[JSInvokable("DifferentMethodName")]

Rufen Sie im Aufruf von DotNet.invokeMethodAsync (serverseitige oder clientseitige Komponenten) oder DotNet.invokeMethod (nur clientseitige Komponenten) DifferentMethodName auf, um die .NET-Methode ReturnArrayAsync auszuführen:

  • DotNet.invokeMethodAsync('BlazorSample', 'DifferentMethodName');
  • DotNet.invokeMethod('BlazorSample', 'DifferentMethodName'); (nur clientseitige Komponenten)

Hinweis

Im Beispiel für die ReturnArrayAsync-Methode in diesem Abschnitt wird das Ergebnis einer Taskohne Verwendung der expliziten C#-Schlüsselwörter async und await zurückgegeben. Das Codieren von Methoden mit async und await ist typisch für Methoden, die das Schlüsselwort await verwenden, um den Wert asynchroner Vorgänge zurückzugeben.

ReturnArrayAsync-Methode mit den Schlüsselwörtern async und await:

[JSInvokable]
public static async Task<int[]> ReturnArrayAsync()
{
    return await Task.FromResult(new int[] { 1, 2, 3 });
}

Weitere Informationen finden Sie unter Asynchrone Programmierung mit Async und Await in der C#-Anleitung.

Erstellen von Verweisen auf JavaScript-Objekte und -Daten für die Übergabe an .NET

Rufen Sie DotNet.createJSObjectReference(jsObject) auf, um einen JS-Objektverweis zu erstellen, der an .NET übergeben werden kann, wobei jsObject das JS Object-Objekt ist, das zum Erstellen des JS-Objektverweises verwendet wird. Im folgenden Beispiel wird ein Verweis auf das nicht serialisierbare window-Objekt an .NET übergeben, das es in der C#-Methode ReceiveWindowObject als IJSObjectReference empfängt:

DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', 
  DotNet.createJSObjectReference(window));
[JSInvokable]
public static void ReceiveWindowObject(IJSObjectReference objRef)
{
    ...
}

Im Beispiel oben steht der Platzhalter {ASSEMBLY NAME} für den Namespace der App.

Hinweis

Das obige Beispiel erfordert kein Löschen von JSObjectReference, da in JS kein Verweis auf das window-Objekt gespeichert wird.

Die Beibehaltung eines Verweises auf einen JSObjectReference erfordert die Löschung des Verweises, um einen JS-Arbeitsspeicherverlust auf dem Client zu vermeiden. Mit dem folgenden Beispiel wird der vorherige Code so umgestaltet, dass ein Verweis auf den JSObjectReference erfasst wird. Danach erfolgt ein Aufruf von DotNet.disposeJSObjectReference(), um den Verweis zu löschen:

var jsObjectReference = DotNet.createJSObjectReference(window);

DotNet.invokeMethodAsync('{ASSEMBLY NAME}', 'ReceiveWindowObject', jsObjectReference);

DotNet.disposeJSObjectReference(jsObjectReference);

Im Beispiel oben steht der Platzhalter {ASSEMBLY NAME} für den Namespace der App.

Rufen Sie auf DotNet.createJSStreamReference(streamReference), um einen JS-Datenstromverweis zu erstellen, damit dieser an .NET übergeben werden kann, wobei streamReference ein ArrayBuffer, Blob oder ein beliebiges typisiertes Array wie Uint8Array oder Float32Array zum Erstellen des JS-Datenstromverweises ist.

Aufrufen einer .NET-Instanzmethode

So rufen Sie eine .NET-Instanzmethode in JavaScript (JS) auf:

  • Übergeben Sie die .NET-Instanz als Verweis auf JS, indem Sie die Instanz in DotNetObjectReference einschließen und dafür Create aufrufen.

  • Rufen Sie eine .NET-Instanzmethode über JS mit invokeMethodAsync (empfohlen) oder invokeMethod (nur clientseitige Komponenten) aus dem übergebenen .NET-Objektverweis (DotNetObjectReference) auf. Übergeben Sie den Bezeichner der .NET-Instanzmethode und alle vorhandenen Argumente. Die .NET-Instanz kann auch als Argument übergeben werden, wenn andere .NET-Methoden von JS aus aufgerufen werden.

    Siehe folgendes Beispiel:

    • dotNetHelper ist ein DotNetObjectReference.
    • Der Platzhalter {.NET METHOD ID} ist der Bezeichner der .NET-Methode.
    • Der Platzhalter {ARGUMENTS} steht für optionale, durch Komma getrennte Argumente, die an die Methode übergeben werden sollen, von denen jedes JSON-serialisierbar sein muss.
    dotNetHelper.invokeMethodAsync('{.NET METHOD ID}', {ARGUMENTS});
    

    Hinweis

    invokeMethodAsync und invokeMethod akzeptieren beim Aufrufen einer Instanzmethode keinen Assemblynamenparameter.

    invokeMethodAsync gibt ein JS Promise zurück, das das Ergebnis des Vorgangs darstellt. invokeMethod (nur clientseitige Komponenten) gibt das Ergebnis des Vorgangs zurück.

    Wichtig

    Für serverseitige Komponenten wird die asynchrone Funktion (invokeMethodAsync) anstelle der synchronen Version (invokeMethod) empfohlen.

  • Löschen Sie DotNetObjectReference.

In den folgenden Abschnitten dieses Artikels werden verschiedene Ansätze zum Aufrufen einer .NET-Instanzmethode veranschaulicht:

Umgehen der Kürzung von über JavaScript aufrufbaren .NET-Methoden

Dieser Abschnitt gilt für clientseitige Apps mit aktivierter Ahead-of-time-Kompilierung und erneuter Verknüpfung der Runtime.

Mehrere Beispiele in den folgenden Abschnitten basieren auf einem Klasseninstanzansatz, bei dem die mit dem [JSInvokable]-Attribut gekennzeichnete und durch JavaScript aufrufbare .NET-Methode ein Member einer Klasse ist, bei der es sich nicht um eine Razor-Komponente handelt. Wenn sich solche .NET-Methoden in einer Razor-Komponente befinden, sind sie vor dem erneuten Verknüpfen der Runtime und dem Kürzen geschützt. Um die .NET-Methoden vor dem Kürzen außerhalb von Razor-Komponenten zu schützen, implementieren Sie die Methoden mit dem DynamicDependency-Attribut für den Konstruktor der Klasse. Dies wird im folgenden Beispiel veranschaulicht:

using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;

public class ExampleClass {

    [DynamicDependency(nameof(ExampleJSInvokableMethod))]
    public ExampleClass()
    {
    }

    [JSInvokable]
    public string ExampleJSInvokableMethod()
    {
        ...
    }
}

Weitere Informationen finden Sie unter Vorbereiten von .NET-Bibliotheken für Kürzungen: DynamicDependency.

Übergeben von DotNetObjectReference an eine einzelne JavaScript-Funktion

Im Beispiel in diesem Abschnitt wird veranschaulicht, wie ein DotNetObjectReferencean eine einzelne JavaScript-Funktion (JS) übergeben wird.

Die folgende sayHello1JS-Funktion empfängt DotNetObjectReference und ruft invokeMethodAsync auf, um die .NET-Methode GetHelloMessage einer Komponente aufzurufen:

<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Hinweis

Allgemeine Anleitungen zu JS Standort und unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET Core Blazor -Apps.

Im vorherigen Beispiel ist der Variablenname dotNetHelper willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Für die folgende -Komponente gilt:

  • Die Komponente verfügt über eine .NET-Methode mit dem Namen GetHelloMessage, die von JS aufgerufen werden kann.
  • Bei Auswahl der Schaltfläche Trigger .NET instance method wird die JS-Funktion sayHello1 mit DotNetObjectReference aufgerufen.
  • sayHello1:
    • Ruft GetHelloMessage auf und empfängt die resultierende Nachricht.
    • Gibt die resultierende Nachricht an die aufrufende TriggerDotNetInstanceMethod-Methode zurück.
  • Die von sayHello1 in result zurückgegebene Nachricht wird dem Benutzer angezeigt.
  • Um einen Arbeitsspeicherverlust zu vermeiden und eine automatische Speicherbereinigung zu ermöglichen, wird der von DotNetObjectReference erstellte .NET-Objektverweis in der Dispose-Methode gelöscht.

CallDotnet2.razor:

@page "/call-dotnet-2"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET 2</PageTitle>

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotnet2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

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

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

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

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample2>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

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

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

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

CallDotNetExample2.razor:

@page "/call-dotnet-example-2"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 2</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample2> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

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

Im vorherigen Beispiel ist der Variablenname dotNetHelper willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Verwenden Sie den folgenden Leitfaden, um Argumente an eine Instanzmethode zu übergeben:

Fügen Sie Parameter zum .NET-Methodenaufruf hinzu. Im folgenden Beispiel wird ein Name an die Methode übergeben. Fügen Sie der Liste bei Bedarf weitere Parameter hinzu.

<script>
  window.sayHello2 = (dotNetHelper, name) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage', name);
  };
</script>

Im vorherigen Beispiel ist der Variablenname dotNetHelper willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Übergeben Sie die Parameterliste an die .NET-Methode.

CallDotnet3.razor:

@page "/call-dotnet-3"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET 3</PageTitle>

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotnet3>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

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

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample3>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

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

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private DotNetObjectReference<CallDotNetExample3>? objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

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

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample3> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

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

CallDotNetExample3.razor:

@page "/call-dotnet-example-3"
@implements IDisposable
@inject IJSRuntime JS

<h1>Call .NET Example 3</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private DotNetObjectReference<CallDotNetExample3> objRef;

    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

    public async Task TriggerDotNetInstanceMethod()
    {
        result = await JS.InvokeAsync<string>("sayHello2", objRef, name);
    }

    [JSInvokable]
    public string GetHelloMessage(string passedName) => $"Hello, {passedName}!";

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

Im vorherigen Beispiel ist der Variablenname dotNetHelper willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Übergeben von DotNetObjectReference an eine Klasse mit mehreren JavaScript-Funktionen

Im Beispiel in diesem Abschnitt wird veranschaulicht, wie ein DotNetObjectReferencean eine JavaScript-Klasse (JS) mit mehreren Funktionen übergeben wird.

Erstellen und übergeben Sie einen DotNetObjectReference aus der OnAfterRenderAsync-Lebenszyklusmethode an eine JS-Klasse, damit er von mehreren Funktionen verwendet werden kann. Stellen Sie sicher, dass der .NET-Code DotNetObjectReference wie im folgenden Beispiel gezeigt verwirft.

In der folgenden Komponente rufen die Trigger JS function-Schaltflächen JS-Funktionen auf, indem sie die JSonclick-Eigenschaft festlegen, nicht das @onclick-Anweisungsattribut von Blazor.

CallDotNetExampleOneHelper.razor:

@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET Example</PageTitle>

<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>

<p>
    <label>
        Message: <input @bind="name" />
    </label>
</p>

<p>
    <button onclick="GreetingHelpers.sayHello()">
        Trigger JS function <code>sayHello</code>
    </button>
</p>

<p>
    <button onclick="GreetingHelpers.welcomeVisitor()">
        Trigger JS function <code>welcomeVisitor</code>
    </button>
</p>

@code {
    private string? name;
    private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetHelper = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", 
                dotNetHelper);
        }
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    [JSInvokable]
    public string GetWelcomeMessage() => $"Welcome, {name}!";

    public void Dispose()
    {
        dotNetHelper?.Dispose();
    }
}
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET Example</PageTitle>

<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>

<p>
    <label>
        Message: <input @bind="name" />
    </label>
</p>

<p>
    <button onclick="GreetingHelpers.sayHello()">
        Trigger JS function <code>sayHello</code>
    </button>
</p>

<p>
    <button onclick="GreetingHelpers.welcomeVisitor()">
        Trigger JS function <code>welcomeVisitor</code>
    </button>
</p>

@code {
    private string? name;
    private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetHelper = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", 
                dotNetHelper);
        }
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    [JSInvokable]
    public string GetWelcomeMessage() => $"Welcome, {name}!";

    public void Dispose()
    {
        dotNetHelper?.Dispose();
    }
}
@page "/call-dotnet-example-one-helper"
@implements IDisposable
@inject IJSRuntime JS

<PageTitle>Call .NET Example</PageTitle>

<h1>Pass <code>DotNetObjectReference</code> to a JavaScript class</h1>

<p>
    <label>
        Message: <input @bind="name" />
    </label>
</p>

<p>
    <button onclick="GreetingHelpers.sayHello()">
        Trigger JS function <code>sayHello</code>
    </button>
</p>

<p>
    <button onclick="GreetingHelpers.welcomeVisitor()">
        Trigger JS function <code>welcomeVisitor</code>
    </button>
</p>

@code {
    private string name;
    private DotNetObjectReference<CallDotNetExampleOneHelper>? dotNetHelper;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetHelper = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("GreetingHelpers.setDotNetHelper", 
                dotNetHelper);
        }
    }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {name}!";

    [JSInvokable]
    public string GetWelcomeMessage() => $"Welcome, {name}!";

    public void Dispose()
    {
        if (dotNetHelper is not null)
        {
            dotNetHelper.Dispose();
        }
    }
}

Im vorherigen Beispiel:

  • JS ist eine eingefügte IJSRuntime-Instanz. IJSRuntime wird vom Blazor-Framework registriert.
  • Der Variablenname dotNetHelper ist willkürlich gewählt und kann in einen beliebigen Namen geändert werden.
  • Die Komponente muss DotNetObjectReference explizit verwerfen, um die Garbage Collection zuzulassen und einen Speicherverlust zu verhindern.
<script>
  class GreetingHelpers {
    static dotNetHelper;

    static setDotNetHelper(value) {
      GreetingHelpers.dotNetHelper = value;
    }

    static async sayHello() {
      const msg = 
        await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetHelloMessage');
      alert(`Message from .NET: "${msg}"`);
    }

    static async welcomeVisitor() {
      const msg = 
        await GreetingHelpers.dotNetHelper.invokeMethodAsync('GetWelcomeMessage');
      alert(`Message from .NET: "${msg}"`);
    }
  }
    
  window.GreetingHelpers = GreetingHelpers;
</script>

Hinweis

Allgemeine Anleitungen zum JSSpeicherort und zu unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET CoreBlazor-Apps in ASP.NET Core.

Im vorherigen Beispiel:

  • Die GreetingHelpers-Klasse wird dem window-Objekt hinzugefügt, um die Klasse global zu definieren, wodurch Blazor sie für JS-Interop suchen kann.
  • Der Variablenname dotNetHelper ist willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Aufrufen generischer .NET-Klassenmethoden

JavaScript-Funktionen (JS) können generische .NET-Klassenmethoden aufrufen, wobei eine JS-Funktion eine .NET-Methode einer generischen Klasse aufruft.

In der folgenden generischen Typklasse (GenericType<TValue>) gilt:

  • Die Klasse verfügt über einen einzelnen Typparameter (TValue) mit einer einzelnen generischen Value-Eigenschaft.
  • Die Klasse verfügt über zwei nicht generische Methoden, die mit dem [JSInvokable]-Attribut gekennzeichnet sind, jeweils mit einem generischen Typparameter namens newValue:
    • Update aktualisiert synchron den Wert von Value aus newValue.
    • UpdateAsync aktualisiert den Wert von Value aus newValue asynchron, nachdem ein awaitable-Task mit Task.Yield erstellt wurde, der asynchron an den aktuellen Kontext zurückgibt, wenn gewartet wird.
  • Jede der Klassenmethoden schreibt den Typ von TValue und den Wert von Value in die Konsole. Das Schreiben in die Konsole dient nur zu Demonstrationszwecken. Produktions-Apps vermeiden in der Regel das Schreiben in die Konsole zugunsten von App-Protokollierung. Weitere Informationen finden Sie unter ASP.NET CoreBlazor-Protokollierung und Protokollierung in .NET Core und ASP.NET Core.

Hinweis

Offene generische Typen und Methoden geben keine Typen für Typplatzhalter an. Im Gegensatz dazu geben geschlossene Generics Typen für alle Typplatzhalter an. Die Beispiele in diesem Abschnitt veranschaulichen geschlossene Generics, aber das Aufrufen von JS-Interop--Instanzmethoden mit offenen Generics wird unterstützt. Die Verwendung von offenen Generics wird für statische .NET-Methodenaufrufenicht unterstützt, die weiter oben in diesem Artikel beschrieben wurden.

Weitere Informationen finden Sie in den folgenden Artikeln:

GenericType.cs:

using Microsoft.JSInterop;

public class GenericType<TValue>
{
    public TValue? Value { get; set; }

    [JSInvokable]
    public void Update(TValue newValue)
    {
        Value = newValue;

        Console.WriteLine($"Update: GenericType<{typeof(TValue)}>: {Value}");
    }

    [JSInvokable]
    public async void UpdateAsync(TValue newValue)
    {
        await Task.Yield();
        Value = newValue;

        Console.WriteLine($"UpdateAsync: GenericType<{typeof(TValue)}>: {Value}");
    }
}

Für die folgende invokeMethodsAsync-Funktion gilt Folgendes:

  • Die Methoden Update und UpdateAsync der generischen Typklasse werden mit Argumenten aufgerufen, die Zeichenfolgen und Zahlen darstellen.
  • Clientseitige Apps unterstützen das synchrone Aufrufen von .NET-Methoden mit invokeMethod. syncInterop empfängt einen booleschen Wert, der angibt, ob der Client über JS-Interoperabilität verfügt. Wenn syncInteroptrue ist, wird invokeMethod sicher aufgerufen. Hat syncInterop den Wert false, wird nur die asynchrone Funktion invokeMethodAsync aufgerufen, da die JS-Interoperabilität in einer serverseitigen Komponente ausgeführt wird.
  • Zu Demonstrationszwecken werden der DotNetObjectReference-Funktionsaufruf (invokeMethod oder invokeMethodAsync), die aufgerufene .NET-Methode (Update oder UpdateAsync) und das Argument in die Konsole geschrieben. Die Argumente verwenden eine Zufallszahl, um den Abgleich des JS-Funktionsaufrufs mit dem .NET-Methodenaufruf zu ermöglichen (auf der .NET-Seite auch in die Konsole geschrieben). Produktionscode schreibt in der Regel weder auf dem Client noch auf dem Server in die Konsole. Produktions-Apps verwenden in der Regel App-Protokollierung. Weitere Informationen finden Sie unter ASP.NET CoreBlazor-Protokollierung und Protokollierung in .NET Core und ASP.NET Core.
<script>
  const randomInt = () => Math.floor(Math.random() * 99999);

  window.invokeMethodsAsync = async (syncInterop, dotNetHelper1, dotNetHelper2) => {
    var n = randomInt();
    console.log(`JS: invokeMethodAsync:Update('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('Update', `string ${n}`);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync('string ${n}')`);
    await dotNetHelper1.invokeMethodAsync('UpdateAsync', `string ${n}`);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update('string ${n}')`);
      dotNetHelper1.invokeMethod('Update', `string ${n}`);
    }

    n = randomInt();
    console.log(`JS: invokeMethodAsync:Update(${n})`);
    await dotNetHelper2.invokeMethodAsync('Update', n);

    n = randomInt();
    console.log(`JS: invokeMethodAsync:UpdateAsync(${n})`);
    await dotNetHelper2.invokeMethodAsync('UpdateAsync', n);

    if (syncInterop) {
      n = randomInt();
      console.log(`JS: invokeMethod:Update(${n})`);
      dotNetHelper2.invokeMethod('Update', n);
    }
  };
</script>

Hinweis

Allgemeine Anleitungen zum JSSpeicherort und zu unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET CoreBlazor-Apps in ASP.NET Core.

In der folgenden GenericsExample-Komponente:

  • Bei Auswahl der Schaltfläche Invoke Interop wird die JS-Funktion invokeMethodsAsync aufgerufen.
  • Ein Paar von DotNetObjectReference-Typen wird erstellt und an die JS-Funktion für Instanzen von GenericType als string und int übergeben.

GenericsExample.razor:

@page "/generics-example"
@using System.Runtime.InteropServices
@implements IDisposable
@inject IJSRuntime JS

<p>
    <button @onclick="InvokeInterop">Invoke Interop</button>
</p>

<ul>
    <li>genericType1: @genericType1?.Value</li>
    <li>genericType2: @genericType2?.Value</li>
</ul>

@code {
    private GenericType<string> genericType1 = new() { Value = "string 0" };
    private GenericType<int> genericType2 = new() { Value = 0 };
    private DotNetObjectReference<GenericType<string>>? objRef1;
    private DotNetObjectReference<GenericType<int>>? objRef2;

    protected override void OnInitialized()
    {
        objRef1 = DotNetObjectReference.Create(genericType1);
        objRef2 = DotNetObjectReference.Create(genericType2);
    }

    public async Task InvokeInterop()
    {
        var syncInterop =
            RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));

        await JS.InvokeVoidAsync(
            "invokeMethodsAsync", syncInterop, objRef1, objRef2);
    }

    public void Dispose()
    {
        objRef1?.Dispose();
        objRef2?.Dispose();
    }
}
@page "/generics-example"
@using System.Runtime.InteropServices
@implements IDisposable
@inject IJSRuntime JS

<p>
    <button @onclick="InvokeInterop">Invoke Interop</button>
</p>

<ul>
    <li>genericType1: @genericType1?.Value</li>
    <li>genericType2: @genericType2?.Value</li>
</ul>

@code {
    private GenericType<string> genericType1 = new() { Value = "string 0" };
    private GenericType<int> genericType2 = new() { Value = 0 };
    private DotNetObjectReference<GenericType<string>>? objRef1;
    private DotNetObjectReference<GenericType<int>>? objRef2;

    protected override void OnInitialized()
    {
        objRef1 = DotNetObjectReference.Create(genericType1);
        objRef2 = DotNetObjectReference.Create(genericType2);
    }

    public async Task InvokeInterop()
    {
        var syncInterop =
            RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));

        await JS.InvokeVoidAsync(
            "invokeMethodsAsync", syncInterop, objRef1, objRef2);
    }

    public void Dispose()
    {
        objRef1?.Dispose();
        objRef2?.Dispose();
    }
}

Im vorangehenden Beispiel ist JS eine injizierte IJSRuntime Instanz. IJSRuntime wird vom Blazor-Framework registriert.

Im Folgenden wird die typische Ausgabe des vorherigen Beispiels veranschaulicht, wenn die Schaltfläche Invoke Interop in einer clientseitigen Komponente ausgewählt wird:

JS: invokeMethodAsync:Update('string 37802')
.NET: Update: GenericType<System.String>: string 37802
JS: invokeMethodAsync:UpdateAsync('string 53051')
JS: invokeMethod:Update('string 26784')
.NET: Update: GenericType<System.String>: string 26784
JS: invokeMethodAsync:Update(14107)
.NET: Update: GenericType<System.Int32>: 14107
JS: invokeMethodAsync:UpdateAsync(48995)
JS: invokeMethod:Update(12872)
.NET: Update: GenericType<System.Int32>: 12872
.NET: UpdateAsync: GenericType<System.String>: string 53051
.NET: UpdateAsync: GenericType<System.Int32>: 48995

Wenn das vorherige Beispiel in einer serverseitigen Komponente implementiert wird, werden die synchronen Aufrufe mit invokeMethod vermieden. Für serverseitige Komponenten wird die asynchrone Funktion (invokeMethodAsync) anstelle der synchronen Version (invokeMethod) empfohlen.

Typische Ausgabe einer serverseitigen Komponente:

JS: invokeMethodAsync:Update('string 34809')
.NET: Update: GenericType<System.String>: string 34809
JS: invokeMethodAsync:UpdateAsync('string 93059')
JS: invokeMethodAsync:Update(41997)
.NET: Update: GenericType<System.Int32>: 41997
JS: invokeMethodAsync:UpdateAsync(24652)
.NET: UpdateAsync: GenericType<System.String>: string 93059
.NET: UpdateAsync: GenericType<System.Int32>: 24652

Die Ausgabebeispiele oben veranschaulichen, dass asynchrone Methoden in einer beliebigen Reihenfolge ausgeführt und abgeschlossen werden, abhängig von mehreren Faktoren, einschließlich Threadplanung und der Geschwindigkeit der Methodenausführung. Es ist nicht möglich, die Reihenfolge des Abschlusses für asynchrone Methodenaufrufe zuverlässig vorherzusagen.

Beispiele für Klasseninstanzen

Die folgende JS-Funktion sayHello1:

  • Ruft die .NET-Methode GetHelloMessage für das übergebene DotNetObjectReference auf.
  • Gibt die Nachricht von GetHelloMessage an den sayHello1-Aufrufer zurück.
<script>
  window.sayHello1 = (dotNetHelper) => {
    return dotNetHelper.invokeMethodAsync('GetHelloMessage');
  };
</script>

Hinweis

Allgemeine Anleitungen zum JSSpeicherort und zu unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET CoreBlazor-Apps in ASP.NET Core.

Im vorherigen Beispiel ist der Variablenname dotNetHelper willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Die folgende HelloHelper-Klasse verfügt über eine .NET-Methode mit dem Namen GetHelloMessage, die von JS aufgerufen werden kann. Wenn HelloHelper erstellt wird, wird der Name in der Name-Eigenschaft verwendet, um eine Nachricht von GetHelloMessage zurückzugeben.

HelloHelper.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class HelloHelper(string? name)
{
    public string? Name { get; set; } = name ?? "No Name";

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string? name)
    {
        Name = name ?? "No Name";
    }

    public string? Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string? name)
    {
        Name = name ?? "No Name";
    }

    public string? Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}
using Microsoft.JSInterop;

public class HelloHelper
{
    public HelloHelper(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    [JSInvokable]
    public string GetHelloMessage() => $"Hello, {Name}!";
}

Die CallHelloHelperGetHelloMessage-Methode in der folgenden JsInteropClasses3-Klasse ruft die JS-Funktion sayHello1 mit einer neuen Instanz von HelloHelper auf.

JsInteropClasses3.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class JsInteropClasses3(IJSRuntime js)
{
    private readonly IJSRuntime js = js;

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string? name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}
using System.Threading.Tasks;
using Microsoft.JSInterop;

public class JsInteropClasses3
{
    private readonly IJSRuntime js;

    public JsInteropClasses3(IJSRuntime js)
    {
        this.js = js;
    }

    public async ValueTask<string> CallHelloHelperGetHelloMessage(string name)
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        return await js.InvokeAsync<string>("sayHello1", objRef);
    }
}

Um einen Arbeitsspeicherverlust zu vermeiden und die Garbage Collection zuzulassen, wird der von DotNetObjectReference erstellte .NET-Objektverweis gelöscht, wenn der Objektverweis nicht mit der using var-Syntax übereinstimmt.

Bei Auswahl der Schaltfläche Trigger .NET instance method in der folgenden Komponente, wird JsInteropClasses3.CallHelloHelperGetHelloMessage mit dem Wert von name aufgerufen.

CallDotnet4.razor:

@page "/call-dotnet-4"
@inject IJSRuntime JS

<PageTitle>Call .NET 4</PageTitle>

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private JsInteropClasses3? jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private JsInteropClasses3? jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;
    private JsInteropClasses3? jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        if (jsInteropClasses is not null)
        {
            result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
        }
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }
}

CallDotNetExample4.razor:

@page "/call-dotnet-example-4"
@inject IJSRuntime JS

<h1>Call .NET Example 4</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;
    private JsInteropClasses3 jsInteropClasses;

    protected override void OnInitialized()
    {
        jsInteropClasses = new JsInteropClasses3(JS);
    }

    private async Task TriggerDotNetInstanceMethod()
    {
        result = await jsInteropClasses.CallHelloHelperGetHelloMessage(name);
    }
}

Die folgende Abbildung zeigt die gerenderte Komponente mit dem Namen Amy Pond im Name-Feld. Nach Auswahl der Schaltfläche wird Hello, Amy Pond! auf der Benutzeroberfläche angezeigt:

Gerenderte CallDotNetExample4-Komponente – Beispiel

Das obige Muster, das in der Klasse JsInteropClasses3 veranschaulicht wird, kann auch vollständig in einer Komponente implementiert werden.

CallDotnet5.razor:

@page "/call-dotnet-5"
@inject IJSRuntime JS

<PageTitle>Call .NET 5</PageTitle>

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string? name;
    private string? result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

CallDotNetExample5.razor:

@page "/call-dotnet-example-5"
@inject IJSRuntime JS

<h1>Call .NET Example 5</h1>

<p>
    <label>
        Name: <input @bind="name" />
    </label>
</p>

<p>
    <button @onclick="TriggerDotNetInstanceMethod">
        Trigger .NET instance method
    </button>
</p>

<p>
    @result
</p>

@code {
    private string name;
    private string result;

    public async Task TriggerDotNetInstanceMethod()
    {
        using var objRef = DotNetObjectReference.Create(new HelloHelper(name));
        result = await JS.InvokeAsync<string>("sayHello1", objRef);
    }
}

Um einen Arbeitsspeicherverlust zu vermeiden und die Garbage Collection zuzulassen, wird der von DotNetObjectReference erstellte .NET-Objektverweis gelöscht, wenn der Objektverweis nicht mit der using var-Syntax übereinstimmt.

Die von der Komponente angezeigte Ausgabe lautet Hello, Amy Pond!, wenn der Name Amy Pond im Feld name angegeben wird.

In der obigen -Komponente wird der .NET-Objektverweis gelöscht. Wenn eine Klasse oder Komponente DotNetObjectReference nicht löscht, muss es vom Client gelöscht werden, indem dispose für das übergebene DotNetObjectReference aufgerufen wird:

window.{JS FUNCTION NAME} = (dotNetHelper) => {
  dotNetHelper.invokeMethodAsync('{.NET METHOD ID}');
  dotNetHelper.dispose();
}

Im vorherigen Beispiel:

  • Der Platzhalter {JS FUNCTION NAME} ist der Name für die JS-Funktion.
  • Der Variablenname dotNetHelper ist willkürlich gewählt und kann in einen beliebigen Namen geändert werden.
  • Der Platzhalter {.NET METHOD ID} ist der Bezeichner der .NET-Methode.

Hilfsklasse für die .NET-Instanzmethode einer Komponente

Eine Hilfsklasse kann eine .NET-Instanzmethode als Action aufrufen. Hilfsklassen sind in folgenden Szenarios nützlich:

  • Auf einer Seite werden mehrere Komponenten desselben Typs gerendert.
  • In serverseitigen Apps, in denen mehrere Benutzer*innen gleichzeitig die gleiche Komponente verwenden.

Im folgenden Beispiel:

  • Die Komponente enthält mehrere ListItem1-Komponenten, bei denen es sich um freigegebene Komponenten im Shared-Ordner der App handelt.
  • Alle ListItem1-Komponenten bestehen aus einer Nachricht und einer Schaltfläche.
  • Wenn auf die Schaltfläche einer ListItem1-Komponente geklickt wird, ändert die UpdateMessage-Methode des ListItem1-Elements den Text des Listenelements und blendet die Schaltfläche aus.

Die folgende MessageUpdateInvokeHelper-Klasse umfasst eine.NET-Methode UpdateMessageCaller, die von JS aufgerufen werden kann, um die Action aufzurufen, die beim Instanziieren der Klasse angegeben wurde.

MessageUpdateInvokeHelper.cs:

using Microsoft.JSInterop;

namespace BlazorSample;

public class MessageUpdateInvokeHelper(Action action)
{
    private readonly Action action = action;

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}
using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    private Action action;

    public MessageUpdateInvokeHelper(Action action)
    {
        this.action = action;
    }

    [JSInvokable]
    public void UpdateMessageCaller()
    {
        action.Invoke();
    }
}

Die folgende JS-Funktion updateMessageCaller ruft die .NET-Methode UpdateMessageCaller auf.

<script>
  window.updateMessageCaller = (dotNetHelper) => {
    dotNetHelper.invokeMethodAsync('UpdateMessageCaller');
    dotNetHelper.dispose();
  }
</script>

Hinweis

Allgemeine Anleitungen zum JSSpeicherort und zu unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET CoreBlazor-Apps in ASP.NET Core.

Im vorherigen Beispiel ist der Variablenname dotNetHelper willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Die folgende ListItem1-Komponente ist eine freigegebene Komponente, die in einer übergeordneten Komponente mehrmals verwendet werden kann und Listenelemente (<li>...</li>) für eine HTML-Liste (<ul>...</ul> oder <ol>...</ol>) erstellt. Jede ListItem1-Komponenteninstanz richtet eine Instanz von MessageUpdateInvokeHelper ein, wobei eine Action auf die zugehörige UpdateMessage-Methode festgelegt ist.

Bei Auswahl der Schaltfläche InteropCall einer ListItem1-Komponente wird updateMessageCaller mit einem erstellten DotNetObjectReference für die MessageUpdateInvokeHelper-Instanz aufgerufen. Dadurch kann das Framework UpdateMessageCaller für die MessageUpdateInvokeHelper-Instanz für das betreffende ListItem1 aufrufen. Das übergebene DotNetObjectReference wird in JS (dotNetHelper.dispose()) gelöscht.

ListItem1.razor:

@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        if (messageUpdateInvokeHelper is not null)
        {
            await JS.InvokeVoidAsync("updateMessageCaller",
                DotNetObjectReference.Create(messageUpdateInvokeHelper));
        }
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        if (messageUpdateInvokeHelper is not null)
        {
            await JS.InvokeVoidAsync("updateMessageCaller",
                DotNetObjectReference.Create(messageUpdateInvokeHelper));
        }
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper? messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        if (messageUpdateInvokeHelper is not null)
        {
            await JS.InvokeVoidAsync("updateMessageCaller",
                DotNetObjectReference.Create(messageUpdateInvokeHelper));
        }
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        await JS.InvokeVoidAsync("updateMessageCaller",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}
@inject IJSRuntime JS

<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>

@code {
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }

    protected async Task InteropCall()
    {
        await JS.InvokeVoidAsync("updateMessageCaller",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }

    private void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}

StateHasChanged wird aufgerufen, um die Benutzeroberfläche zu aktualisieren, wenn message in UpdateMessage festgelegt wird. Wenn StateHasChanged nicht aufgerufen wird, kann Blazor nicht wissen, dass die Benutzeroberfläche aktualisiert werden soll, wenn Action aufgerufen wird.

Die folgende übergeordnete Komponente enthält vier Listenelemente, die jeweils eine Instanz der ListItem1-Komponente sind.

CallDotnet6.razor:

@page "/call-dotnet-6"

<PageTitle>Call .NET 6</PageTitle>

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

CallDotNetExample6.razor:

@page "/call-dotnet-example-6"

<h1>Call .NET Example 6</h1>

<ul>
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
    <ListItem1 />
</ul>

Die folgende Abbildung zeigt die gerenderte übergeordnete Komponente, nachdem die zweite InteropCall-Schaltfläche ausgewählt wurde:

  • Die zweite ListItem1-Komponente hat die Meldung UpdateMessage Called! angezeigt.
  • Die Schaltfläche InteropCall für die zweite ListItem1-Komponente ist nicht sichtbar, da die CSS-Eigenschaft display der Schaltfläche auf none festgelegt ist.

Gerenderte CallDotNetExample6-Komponente – Beispiel

.NET-Methode der Komponenteninstanz, die von DotNetObjectReference aufgerufen wird, die einer Elementeigenschaft zugewiesen ist

Die Zuweisung einer DotNetObjectReference zu einer Eigenschaft eines HTML-Elements ermöglicht das Aufrufen von .NET-Methoden für eine Komponenteninstanz:

Ähnlich wie der im Abschnitt .NET-Methodenhilfsklassen der Komponenteninstanz beschriebene Ansatz ist dieser Ansatz in den folgenden Szenarien nützlich:

  • Auf einer Seite werden mehrere Komponenten desselben Typs gerendert.
  • In serverseitigen Apps, in denen mehrere Benutzer*innen gleichzeitig die gleiche Komponente verwenden.
  • Die .NET-Methode wird aus einem JS-Ereignis (z. B. onclick) und nicht aus einem Blazor-Ereignis (z. B. @onclick) aufgerufen.

Im folgenden Beispiel:

  • Die Komponente enthält mehrere ListItem2-Komponenten, bei denen es sich um freigegebene Komponenten im Shared-Ordner der App handelt.
  • Jede ListItem2 Komponente besteht aus einer Listenelementnachricht <span> und einer zweiten <span> mit einer display CSS-Eigenschaft, die für die Anzeige auf inline-block festgelegt ist.
  • Wenn ein ListItem2 Komponentenlistenelement ausgewählt ist, ändert die UpdateMessage-Methode dieses ListItem2 den Listenelementtext im ersten <span> und blendet den zweiten <span> aus, indem sie seine display-Eigenschaft auf none festlegt.

Mit der folgenden assignDotNetHelperJS-Funktion wird die DotNetObjectReference einem Element in einer Eigenschaft mit dem Namen dotNetHelper zugewiesen:

<script>
  window.assignDotNetHelper = (element, dotNetHelper) => {
    element.dotNetHelper = dotNetHelper;
  }
</script>

Die folgende interopCallJS-Funktion verwendet die DotNetObjectReference für das übergebene Element zum Aufrufen einer .NET-Methode mit dem Namen UpdateMessage:

<script>
  window.interopCall = async (element) => {
    await element.dotNetHelper.invokeMethodAsync('UpdateMessage');
  }
</script>

Hinweis

Allgemeine Anleitungen zum JSSpeicherort und zu unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET CoreBlazor-Apps in ASP.NET Core.

Im vorherigen Beispiel ist der Variablenname dotNetHelper willkürlich gewählt und kann in einen beliebigen Namen geändert werden.

Die folgende ListItem2-Komponente ist eine freigegebene Komponente, die in einer übergeordneten Komponente mehrmals verwendet werden kann und Listenelemente (<li>...</li>) für eine HTML-Liste (<ul>...</ul> oder <ol>...</ol>) erstellt.

Jede ListItem2 Komponenteninstanz ruft die assignDotNetHelperJS-Funktion in OnAfterRenderAsync mit einer Elementreferenz (das erste <span>-Element des Listenelements) und die Komponenteninstanz als eine DotNetObjectReference auf.

Wenn die Nachricht einer ListItem2-Komponente <span> ausgewählt ist, wird interopCall aufgerufen und dadurch das <span>-Element als Parameter (this) übergeben, der die UpdateMessage-.NET-Methode aufruft. In UpdateMessage wird StateHasChanged aufgerufen, um die Benutzeroberfläche zu aktualisieren, wenn message festgelegt und die Eigenschaft display des zweiten <span> aktualisiert wird. Wenn StateHasChanged nicht aufgerufen wird, kann Blazor nicht wissen, dass die Benutzeroberfläche aktualisiert werden soll, wenn die Methode aufgerufen wird.

Die DotNetObjectReference wird verworfen, wenn die Komponente verworfen wird.

ListItem2.razor:

@inject IJSRuntime JS

<li>
    <span style="font-weight:bold;color:@color" @ref="elementRef" 
        onclick="interopCall(this)">
        @message
    </span>
    <span style="display:@display">
        Not Updated Yet!
    </span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";
    private string color = "initial";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        color = "MediumSeaGreen";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2>? objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2> objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

    public void Dispose() => objRef?.Dispose();
}
@inject IJSRuntime JS

<li>
    <span @ref="elementRef" onclick="interopCall(this)">@message</span>
    <span style="display:@display">Not Updated Yet!</span>
</li>

@code {
    private DotNetObjectReference<ListItem2> objRef;
    private ElementReference elementRef;
    private string display = "inline-block";
    private string message = "Select one of these list items.";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            objRef = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("assignDotNetHelper", elementRef, objRef);
        }
    }

    [JSInvokable]
    public void UpdateMessage()
    {
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }

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

Die folgende übergeordnete Komponente enthält vier Listenelemente, die jeweils eine Instanz der ListItem2-Komponente sind.

CallDotnet7.razor:

@page "/call-dotnet-7"

<PageTitle>Call .NET 7</PageTitle>

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

CallDotNetExample7.razor:

@page "/call-dotnet-example-7"

<h1>Call .NET Example 7</h1>

<ul>
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
    <ListItem2 />
</ul>

Synchrone JS-Interoperabilität in clientseitigen Komponenten

Dieser Abschnitt gilt nur für clientseitige Komponenten.

JS-Interop-Aufrufe sind standardmäßig asynchron, und zwar unabhängig davon, ob der aufgerufene Code synchron oder asynchron ist. Aufrufe sind standardmäßig asynchron, um sicherzustellen, dass Komponenten mit serverseitigen und clientseitigen Rendermodi kompatibel sind. Auf dem Server müssen alle JS-Interop-Aufrufe asynchron sein, da sie über eine Netzwerkverbindung gesendet werden.

Wenn Ihre Komponente ausschließlich in WebAssembly ausgeführt wird, können Sie sich für synchrone JS-Interop-Aufrufe entscheiden. Dies ist mit etwas weniger Mehraufwand verbunden als asynchrone Aufrufe und kann zu weniger Renderingzyklen führen, da es keinen Zwischenzustand gibt, während auf die Ergebnisse gewartet wird.

Um in einer clientseitigen Komponente einen synchronen Aufruf von JavaScript an .NET zu richten, verwenden Sie DotNet.invokeMethod anstelle von DotNet.invokeMethodAsync.

Synchrone Aufrufe funktionieren unter folgenden Voraussetzungen:

  • Die Komponente wird nur für die Ausführung in WebAssembly gerendert.
  • Die aufgerufene Funktion gibt synchron einen Wert zurück. Die Funktion ist keine async-Methode und gibt keinen .NET-Task bzw. kein JavaScript-Promise zurück.

JavaScript-Speicherort

Laden Sie JavaScript (JS)-Code mithilfe eines der Ansätze, die im Artikel zum JavaScript-Speicherortbeschrieben werden:

Die Verwendung von JS-Modulen zum Laden von JS wird in diesem Artikel im Abschnitt JavaScript-Isolation in JavaScript-Modulen beschrieben.

Warnung

Platzieren Sie ein <script>-Tag nur in einer Komponentendatei (.razor), wenn die Komponente garantiert statisches serverseitiges Rendering (static sSR) verwendet, da das <script>-Tag nicht dynamisch aktualisiert werden kann.

Warnung

Platzieren Sie kein <script>-Tag in einer Komponentendatei (.razor), weil das <script>-Tag nicht dynamisch aktualisiert werden kann.

JavaScript-Isolierung in JavaScript-Modulen

Blazor aktiviert die JavaScript-Isolation (JS) in JavaScript-Standardmodulen (ECMAScript-Spezifikation). Das Laden von JavaScript-Modulen funktioniert in Blazor genauso wie bei anderen Arten von Web-Apps, und Sie können die Definition von Modulen in Ihrer App anpassen. Eine Anleitung zur Verwendung von JavaScript-Modulen finden Sie in den MDN Web Docs: JavaScript modules.

JS-Isolierung bietet die folgenden Vorteile:

  • Importiertes JS verschmutzt nicht mehr den globalen Namespace.
  • Consumer einer Bibliothek und Komponenten müssen nicht das zugehörige JS importieren.

Weitere Informationen finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core.

Der dynamische Import mit dem import()-Operator wird mit ASP.NET Core und Blazor unterstützt:

if ({CONDITION}) import("/additionalModule.js");

Im vorherigen Beispiel steht der Platzhalter {CONDITION} für eine bedingte Überprüfung, mit der festgestellt wird, ob das Modul geladen werden soll.

Informationen zur Browserkompatibilität finden Sie unter Can I use: JavaScript modules: dynamic import.

Vermeiden von Objektzirkelbezügen

Objekte, die Zirkelbezüge enthalten, können auf dem Client für folgende Vorgänge nicht serialisiert werden:

  • .NET-Methodenaufrufe.
  • JavaScript-Methodenaufrufe von C#, wenn der Rückgabetyp Zirkelbezüge enthält.

Bytearrayunterstützung

Blazor unterstützt optimiertes Bytearray-JavaScript (JS)-Interop, mit dem sich das Codieren/Decodieren von Bytearrays in Base64 vermeiden lässt. Im folgenden Beispiel wird JS-Interop verwendet, um ein Bytearray an .NET zu übergeben.

Stellen Sie eine sendByteArrayJS-Funktion bereit. Die Funktion wird statisch durch eine Schaltfläche in der Komponente aufgerufen, wobei der Assemblynamenparameter im invokeMethodAsync-Aufruf enthalten ist, und gibt keinen Wert zurück:

<script>
  window.sendByteArray = () => {
    const data = new Uint8Array([0x45,0x76,0x65,0x72,0x79,0x74,0x68,0x69,
      0x6e,0x67,0x27,0x73,0x20,0x73,0x68,0x69,0x6e,0x79,0x2c,
      0x20,0x43,0x61,0x70,0x74,0x61,0x69,0x6e,0x2e,0x20,0x4e,
      0x6f,0x74,0x20,0x74,0x6f,0x20,0x66,0x72,0x65,0x74,0x2e]);
    DotNet.invokeMethodAsync('BlazorSample', 'ReceiveByteArray', data)
      .then(str => {
        alert(str);
      });
  };
</script>

Hinweis

Allgemeine Anleitungen zu JS Standort und unseren Empfehlungen für Produktions-Apps finden Sie unter JavaScript-Speicherort in ASP.NET Core Blazor -Apps.

CallDotnet8.razor:

@page "/call-dotnet-8"
@using System.Text

<PageTitle>Call .NET 8</PageTitle>

<h1>Call .NET Example 8</h1>

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

<p>
    Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

CallDotNetExample8.razor:

@page "/call-dotnet-example-8"
@using System.Text

<PageTitle>Call .NET 8</PageTitle>

<h1>Call .NET Example 8</h1>

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

<p>
    Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

CallDotNetExample8.razor:

@page "/call-dotnet-example-8"
@using System.Text

<PageTitle>Call .NET 8</PageTitle>

<h1>Call .NET Example 8</h1>

<p>
    <button onclick="sendByteArray()">Send Bytes</button>
</p>

<p>
    Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
    <a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>

@code {
    [JSInvokable]
    public static Task<string> ReceiveByteArray(byte[] receivedBytes)
    {
        return Task.FromResult(
            Encoding.UTF8.GetString(receivedBytes, 0, receivedBytes.Length));
    }
}

Informationen zur Verwendung eines Bytearrays beim Aufruf von JavaScript aus .NET finden Sie unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core.

Streamen von JavaScript an .NET

Blazor unterstützt das direkte Streamen von Daten aus JavaScript an .NET. Streams werden mithilfe der Microsoft.JSInterop.IJSStreamReference-Schnittstelle angefordert.

Microsoft.JSInterop.IJSStreamReference.OpenReadStreamAsync gibt Stream zurück und verwendet die folgenden Parameter:

  • maxAllowedSize: Maximale Byteanzahl für Lesevorgänge von JavaScript, die Standardeinstellung ist 512.000 Byte, wenn nichts anderes angegeben ist.
  • cancellationToken: Ein CancellationToken zum Abbrechen des Lesevorgangs.

In JavaScript:

function streamToDotNet() {
  return new Uint8Array(10000000);
}

In C#-Code:

var dataReference = 
    await JS.InvokeAsync<IJSStreamReference>("streamToDotNet");
using var dataReferenceStream = 
    await dataReference.OpenReadStreamAsync(maxAllowedSize: 10_000_000);

var outputPath = Path.Combine(Path.GetTempPath(), "file.txt");
using var outputFileStream = File.OpenWrite(outputPath);
await dataReferenceStream.CopyToAsync(outputFileStream);

Im vorherigen Beispiel:

  • JS ist eine eingefügte IJSRuntime-Instanz. IJSRuntime wird vom Blazor-Framework registriert.
  • dataReferenceStream wird auf den Datenträger (in file.txt) geschrieben, auf dem sich der aktuelle temporäre Ordner (GetTempPath) des Benutzers befindet.

Unter Aufrufen von JavaScript-Funktionen über .NET-Methoden in Blazor in ASP.NET Core wird der umgekehrte Vorgang behandelt, nämlich das Streamen aus .NET in JavaScript mithilfe von DotNetStreamReference.

Unter Blazor-Dateiuploads in ASP.NET Core wird das Hochladen einer Datei in Blazor behandelt. Ein Formularbeispiel, das <textarea>-Daten in einer serverseitigen Komponente streamt, finden Sie unter Problembehandlung bei ASP.NET Core Blazor-Formularen.

JavaScript-Interoperabilität mit [JSImport]/[JSExport]

Dieser Abschnitt gilt für clientseitige Komponenten.

Als Alternative zur Interaktion mit JavaScript (JS) in clientseitigen Komponenten, die den JS-Interop-Mechanismus von Blazor basierend auf der IJSRuntime-Schnittstelle verwenden, ist auch eine JS[JSImport]/[JSExport]-Interop-API für Apps verfügbar, die auf .NET 7 oder höher ausgerichtet sind.

Weitere Informationen finden Sie unter JavaScript-Interoperabilität mit JSImport/JSExport mit ASP.NET CoreBlazor.

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 während der Beseitigung von Komponenten

Weitere Informationen finden Sie unter JavaScript-Interoperabilität von Blazor in ASP.NET Core (JS-Interoperabilität).

JavaScript-Interopaufrufe ohne Verbindung

Weitere Informationen finden Sie unter JavaScript-Interoperabilität von Blazor in ASP.NET Core (JS-Interoperabilität).

Zusätzliche Ressourcen