Megosztás a következőn keresztül:


ASP.NET Core Razor-összetevők ártalmatlanítása

Jegyzet

Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.

Figyelmeztetés

A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. Az aktuális kiadást lásd ennek a cikknek a .NET 9-es verziójában.

Ez a cikk a ASP.NET Core-összetevők Razor ártalmatlanítási folyamatát ismerteti és IDisposableismertetiIAsyncDisposable.

Ha egy összetevő IDisposable vagy IAsyncDisposableimplementál, a keretrendszer erőforrás-elidegenítést kér, amikor az összetevőt eltávolítják a felhasználói felületről. Ne támaszkodhat a metódusok végrehajtásának pontos időzítésére. A IAsyncDisposable például azelőtt vagy azután aktiválható, hogy egy aszinkron Task várakozna a OnInitalizedAsync-ben vagy a OnParametersSetAsync-ban, illetve azok meghívásakor vagy befejezésekor. Emellett az objektumelhelyezési kód nem feltételezheti, hogy az inicializálás vagy más életciklus-metódusok során létrehozott objektumok léteznek.

Az összetevőknek nem kell egyszerre implementálniuk IDisposable és IAsyncDisposable. Ha mindkettő implementálva van, a keretrendszer csak az aszinkron túlterhelést hajtja végre.

A fejlesztői kódnak biztosítania kell, hogy IAsyncDisposable implementációk végrehajtása ne tartson sokáig.

További információ: ASP.NET Core Blazor szinkronizálási környezetének bevezető megjegyzései.

JavaScript interop objektumhivatkozások megsemmisítése

A JavaScript (JS) interop cikkek példái a tipikus objektumkezelési mintákat szemléltetik.

JS interop objektumhivatkozások a hivatkozást létrehozó JS interop hívás oldalán található azonosító által kulcsolt térképként vannak implementálva. Ha az objektumelhelyezést a .NET vagy JS oldalról kezdeményezik, Blazor eltávolítja a bejegyzést a térképről, és az objektum szemétként gyűjthető, feltéve, hogy nincs más erős hivatkozás az objektumra.

Legalább a .NET oldalon létrehozott objektumokat mindig megsemmisítse, hogy elkerülje a .NET által felügyelt memória kiszivárgását.

DOM-tisztítási feladatok az összetevők ártalmatlanítása során

További információ: ASP.NET Core Blazor JavaScript-együttműködés (JS interop).

Útmutatásért a JSDisconnectedException tekintetében arra az esetre, amikor egy áramkör le van választva, lásd: ASP.NET Core Blazor JavaScript-interopérabilitás (JS együttműködés). Általános JavaScript-interop hibakezelési útmutatásért tekintse meg az JavaScript-interop szakaszt az ASP.NET Core alkalmazások hibáinak kezelése Blazor című részben.

Szinkron IDisposable

Szinkron ártalmatlanítási feladatokhoz használja a IDisposable.Disposeeszközt.

A következő összetevő:

  • A IDisposable az @implementsRazor irányelv használatával kerül megvalósításra.
  • Eltávolítja a objtípust, amely megvalósítja a IDisposable-et.
  • A null értékű ellenőrzés azért történik, mert obj egy életciklus-metódusban jön létre (nem jelenik meg).
@implements IDisposable

...

@code {
    ...

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

Ha egyetlen objektumot kell megsemmisíteni, a lambda segítségével Dispose meghívásakor megsemmisítheti az objektumot. Az alábbi példa megjelenik a ASP.NET Core Razor összetevő renderelési cikkben, és bemutatja a lambda kifejezés használatát egy Timermegsemmisítéséhez.

TimerDisposal1.razor:

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

<PageTitle>Timer Disposal 1</PageTitle>

<h1>Timer Disposal Example 1</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();
}

TimerDisposal1.razor:

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

<PageTitle>Timer Disposal 1</PageTitle>

<h1>Timer Disposal Example 1</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();
}

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();
}

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();
}

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();
}

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();
}

Jegyzet

Az előző példában a StateHasChanged hívását egy ComponentBase.InvokeAsync hívása csomagolja be, mert a visszahívást a rendszer Blazorszinkronizálási környezetén kívül hívja meg. További információkért lásd: ASP.NET Core Razor komponens megjelenítése.

Ha az objektum életciklus-metódusban (például OnInitialized{Async}) jön létre, a nullhívása előtt ellenőrizze a Dispose.

TimerDisposal2.razor:

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

<PageTitle>Timer Disposal 2</PageTitle>

<h1>Timer Disposal Example 2</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();
}

TimerDisposal2.razor:

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

<PageTitle>Timer Disposal 2</PageTitle>

<h1>Timer Disposal Example 2</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();
}

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();
}

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();
}

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();
}

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();
}

További információ:

Aszinkron IAsyncDisposable

Aszinkron ártalmatlanítási feladatokhoz használja a IAsyncDisposable.DisposeAsync.

A következő összetevő:

  • A IAsyncDisposable az @implementsRazor irányelv használatával kerül megvalósításra.
  • Megsemmisíti a obj-t, amely egy nem felügyelt típus, és megvalósítja a IAsyncDisposable-et.
  • A null értékű ellenőrzés azért történik, mert obj egy életciklus-metódusban jön létre (nem jelenik meg).
@implements IAsyncDisposable

...

@code {
    ...

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

További információ:

A null hozzárendelése az eldobott objektumokhoz

A nullDispose/hívása után általában nincs szükség DisposeAsync hozzárendelésére az eldobott objektumokhoz. A null hozzárendelésének ritka esetei a következők:

  • Ha az objektum típusa rosszul van implementálva, és nem tolerálja a Dispose/DisposeAsyncismételt hívásait, rendeljen null-at a megsemmisítés után, hogy zökkenőmentesen kihagyja a további hívásokat Dispose/DisposeAsync.
  • Ha egy hosszú életű folyamat továbbra is hivatkozik egy megsemmisített objektumra, a null hozzárendelése lehetővé teszi, hogy a szemétgyűjtő felszabadítsa az objektumot annak ellenére, hogy a hosszú élettartamú folyamat hivatkozik rá.

Ezek szokatlan forgatókönyvek. A helyesen implementált és normálisan viselkedő objektumok esetében nincs értelme null hozzárendelni az elvetett objektumokhoz. Azokban a ritka esetekben, amikor egy objektumot nullkell hozzárendelni, javasoljuk, hogy dokumentálja az okot, és keressen olyan megoldást, amely megakadályozza a nullhozzárendelését.

StateHasChanged

Jegyzet

A StateHasChanged hívása Dispose-ben és DisposeAsync-ben nem támogatott. StateHasChanged a renderelő lebontásának részeként lehet meghívni, így a felhasználói felület frissítéseinek kérése ezen a ponton nem támogatott.

Eseménykezelők

Mindig iratkozzon le az eseménykezelők .NET-eseményeiről. Az alábbi Blazor űrlap példák bemutatják, hogyan lehet leiratkozni egy eseménykezelőről a Dispose metódusban.

Privát mező és lambda megközelítés:

@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;
    }
}

Privát módszer megközelítése:

@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;
    }
}

Az EditForm összetevővel és űrlapokkal kapcsolatos további információkért tekintse meg a ASP.NET Core Blazor-űrlapok áttekintési és az Űrlapok csomópont egyéb űrlapcikkeit.

Névtelen függvények, metódusok és kifejezések

Ha névtelen függvényeket , metódusokat vagy kifejezéseket használ, nem szükséges IDisposable és leiratkozási meghatalmazottak implementálása. A meghatalmazott leiratkozásának sikertelensége azonban probléma , ha az eseményt felfedő objektum túllépi a meghatalmazottat regisztráló összetevő élettartamát. Ha ez történik, memóriavesztés következik be, mert a regisztrált meghatalmazott életben tartja az eredeti objektumot. Ezért csak akkor használja a következő módszereket, ha tudja, hogy az eseménymegbízott gyorsan rendelkezik. Ha kétségei vannak az ártalmatlanítást igénylő objektumok élettartamával kapcsolatban, iratkozzon fel egy delegált metódusra, és a korábbi példák szerint megfelelően oldja fel a delegáltat.

Névtelen lambda metódus megközelítés (explicit ártalmatlanítás nem szükséges):

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);
}

Névtelen lambda kifejezési megközelítés (explicit ártalmatlanítás nem szükséges):

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);
}

Az előző kód teljes példája névtelen lambda kifejezésekkel jelenik meg a ASP.NET Core Blazor űrlapok érvényesítési cikkben.

További információ: Nem felügyelt erőforrások tisztítása és az azt követő témakörök a Dispose és DisposeAsync metódusok implementálásával kapcsolatban.

Ártalmatlanítás JS interop során

Csapdázza JSDisconnectedException-t olyan esetekben, amikor a BlazorSignalR áramkör elvesztése megakadályozza a JS interop hívások végrehajtását, és kezeletlen kivételt eredményez.

További információ: