I want to build a website using MudBlazor and C# on .NET 9. On one of the pages, I want to display images, which are loaded in the OnInitializedAsync
event handler. To handle the case when the user leaves the page, I have implemented a Dispose
method.
However, I noticed that both OnInitializedAsync
and Dispose
are executed twice.
Log output:
OnInitializedAsync executed.
Dispose executed.
OnInitializedAsync executed.
Dispose executed.
This behavior only occurs when images are displayed.
I am wondering why this happens and how I can prevent unnecessary disk (or later database) access.
I have already tried using a _isInitialized
flag set to true to prevent a second disposal, but Dispose
is still called twice.
I also attempted using OnAfterRenderAsync
with _isRendered
flag, but since OnAfterRenderAsync
gets triggered multiple times, it is not suitable because the images are reloaded too often.
I came across this thread. I wanted to implement the answers contained there, but the thread is about Blazor in general, and it's from 2019. I don't have the _Host.cshtml
in the project (so, my question is not a duplicate).
I then tried the other answer, using
@rendermode @(new InteractiveServerRenderMode(prerender:false))
This has the effect that the second dispose is no longer called when rendering, otherwise only when you leave the page. Nevertheless, the data is fetched twice.
I use a server-side application. The template when creating the project is this one:
MudBlazor was installed as a Nuget package from the beginning.
I have provided a minimal, runnable example for you to check.
@page "/"
@using MudBlazor
@implements IDisposable
@inject NavigationManager Navigation
<MudLayout>
@if (Foos.Count > 0)
{
<MudGrid Class="mt-4" Spacing="0">
@foreach (Foo f in Foos)
{
@if (f.ImageAsByteArray != null)
{
<MudItem xs="12" sm="6" md="4">
<MudCardContent Dense="true">
<MudImage Src="@($"data:image;base64,{Convert.ToBase64String(f.ImageAsByteArray)}")" Elevation="25" Class="rounded-lg ma-2 d-block mx-auto" Width="350" Height="350" />
</MudCardContent>
</MudItem>
}
}
</MudGrid>
}
</MudLayout>
@code {
private List<Foo> Foos = new List<Foo>();
protected override async Task OnInitializedAsync()
{
Foos = await LoadImages();
Console.WriteLine("OnInitializedAsync executed.");
}
public void Dispose()
{
Console.WriteLine("Dispose executed.");
foreach (Foo foo in Foos)
{
foo.CleanUp();
}
}
private Task<List<Foo>> LoadImages()
{
List<Foo> foos = new List<Foo>();
Foo foo1 = new Foo(System.IO.File.ReadAllBytes(@$"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\Pictures\20190119_131055 2.jpg"));
foos.Add(foo1);
Foo foo2 = new Foo(System.IO.File.ReadAllBytes(@$"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\Pictures\20210117_010401.jpg"));
foos.Add(foo2);
return Task.FromResult(foos);
}
}
namespace BlazorMinimalExample2
{
public class Foo
{
public byte[]? ImageAsByteArray { get; set; }
public Foo(byte[] stuff)
{
this.ImageAsByteArray = stuff;
}
public void CleanUp()
{
if (this.ImageAsByteArray != null)
{
this.ImageAsByteArray = null;
}
}
}
}
I want to build a website using MudBlazor and C# on .NET 9. On one of the pages, I want to display images, which are loaded in the OnInitializedAsync
event handler. To handle the case when the user leaves the page, I have implemented a Dispose
method.
However, I noticed that both OnInitializedAsync
and Dispose
are executed twice.
Log output:
OnInitializedAsync executed.
Dispose executed.
OnInitializedAsync executed.
Dispose executed.
This behavior only occurs when images are displayed.
I am wondering why this happens and how I can prevent unnecessary disk (or later database) access.
I have already tried using a _isInitialized
flag set to true to prevent a second disposal, but Dispose
is still called twice.
I also attempted using OnAfterRenderAsync
with _isRendered
flag, but since OnAfterRenderAsync
gets triggered multiple times, it is not suitable because the images are reloaded too often.
I came across this thread. I wanted to implement the answers contained there, but the thread is about Blazor in general, and it's from 2019. I don't have the _Host.cshtml
in the project (so, my question is not a duplicate).
I then tried the other answer, using
@rendermode @(new InteractiveServerRenderMode(prerender:false))
This has the effect that the second dispose is no longer called when rendering, otherwise only when you leave the page. Nevertheless, the data is fetched twice.
I use a server-side application. The template when creating the project is this one:
MudBlazor was installed as a Nuget package from the beginning.
I have provided a minimal, runnable example for you to check.
@page "/"
@using MudBlazor
@implements IDisposable
@inject NavigationManager Navigation
<MudLayout>
@if (Foos.Count > 0)
{
<MudGrid Class="mt-4" Spacing="0">
@foreach (Foo f in Foos)
{
@if (f.ImageAsByteArray != null)
{
<MudItem xs="12" sm="6" md="4">
<MudCardContent Dense="true">
<MudImage Src="@($"data:image;base64,{Convert.ToBase64String(f.ImageAsByteArray)}")" Elevation="25" Class="rounded-lg ma-2 d-block mx-auto" Width="350" Height="350" />
</MudCardContent>
</MudItem>
}
}
</MudGrid>
}
</MudLayout>
@code {
private List<Foo> Foos = new List<Foo>();
protected override async Task OnInitializedAsync()
{
Foos = await LoadImages();
Console.WriteLine("OnInitializedAsync executed.");
}
public void Dispose()
{
Console.WriteLine("Dispose executed.");
foreach (Foo foo in Foos)
{
foo.CleanUp();
}
}
private Task<List<Foo>> LoadImages()
{
List<Foo> foos = new List<Foo>();
Foo foo1 = new Foo(System.IO.File.ReadAllBytes(@$"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\Pictures\20190119_131055 2.jpg"));
foos.Add(foo1);
Foo foo2 = new Foo(System.IO.File.ReadAllBytes(@$"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\Pictures\20210117_010401.jpg"));
foos.Add(foo2);
return Task.FromResult(foos);
}
}
namespace BlazorMinimalExample2
{
public class Foo
{
public byte[]? ImageAsByteArray { get; set; }
public Foo(byte[] stuff)
{
this.ImageAsByteArray = stuff;
}
public void CleanUp()
{
if (this.ImageAsByteArray != null)
{
this.ImageAsByteArray = null;
}
}
}
}
This is normal behaviour. You are pre-rendering so you everything loads twice.
I'm assuming you are adding @rendermode @(new InteractiveServerRenderMode(prerender:false))
to the page. It won't work as you've already set pre-rendering as true
in App.razor.
If you want to disable pre-rendering globally change it in App.razor:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link href="https://fonts.googleapis/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<link rel="icon" type="image/ico" href="favicon.ico" />
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender:false)" />
</head>
<body>
<Routes @rendermode="new InteractiveServerRenderMode(prerender:false)" />
<script src="_framework/blazor.web.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>