aspnetcore: Improve the Virtualization docs to clarify that placeholder content must be the same size as item content
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
I used this example from the documentation: https://learn.microsoft.com/pl-pl/aspnet/core/blazor/components/virtualization?view=aspnetcore-7.0
private async ValueTask<ItemsProviderResult<Employee>> LoadEmployees(
ItemsProviderRequest request)
{
var numEmployees = Math.Min(request.Count, totalEmployees - request.StartIndex);
var employees = await EmployeesService.GetEmployeesAsync(request.StartIndex,
numEmployees, request.CancellationToken);
return new ItemsProviderResult<Employee>(employees, totalEmployees);
}
My code looks like this:
private async ValueTask<ItemsProviderResult<DTO.Article>> LoadEmployees(ItemsProviderRequest request)
{
// Count = 336
var count = await _search._context.Article.CountAsync(x => x.Status == Models.Article.StatusType.Show);
var numEmployees = Math.Min(request.Count, count - request.StartIndex);
var dto2 = await _search._context.Article
.Include(x => x.Details)
.Include(x => x.Names)
.Include(x => x.Images)
.Where(x => x.Status == Models.Article.StatusType.Show)
.OrderByDescending(x => x.Details.Created)
.Skip(request.StartIndex)
.Take(numEmployees)
.ToListAsync();
var dto3 = dto2.Select(x => new DTO.Article
{
Name = x.Names.OrderByDescending(x => x.Created).Select(x => x.Name).FirstOrDefault() ?? string.Empty,
Title = x.Details.Title,
Description = x.Details.Description,
Created = x.Details.Created,
Tags = x.HashTags.Select(x => x.Tag).Take(3).ToList(),
ImageHead = x.Images.LastOrDefault(x => x.Type == Image.StatusType.Header).Url ?? string.Empty,
}).ToList();
// dto3 = 35 object
// count = 336 int
return new ItemsProviderResult<DTO.Article>(dto3, count);
}
My model:
internal class Article
{
public Guid Id { get; set; }
public string Status { get; set; } = string.Empty;
public string Culture { get; set; } = string.Empty;
public List<string> Cultures { get; set; } = null!;
public string Name { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string Content { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public bool Nsfw { get; set; } = false;
public bool Sponsor { get; set; } = false;
public bool Logged { get; set; } = false;
public bool Premium { get; set; } = false;
public decimal ReadingTime { get; set; }
public DateTime Created { get; set; } = DateTime.UtcNow;
public DateTime Updated { get; set; } = DateTime.UtcNow;
public List<string> Tags { get; set; } = new();
public string ImageHead { get; set; } = string.Empty;
public List<Article> Articles { get; set; } = null!;
}
Razor:
<Row Class="gap-y-[1rem] gap-x-[1%]">
<Virtualize Context="article" ItemsProvider="@LoadEmployees">
<ItemContent>
<Column Class="basis-full">
<Card Title="@article.Title" Link="@($"Article/{article.Name}")" Hover=true>
<CardPicture Url="@article.ImageHead" Class="h-[15rem] xl:h-[20rem]" Sizes=@(new List<CardPicture.Size>{ new () { Width = 512, Height = 288 }})/>
<CardTitle Class="text-left text-xl"/>
<CardContent>
<HashTag Tag="@article.Tags" Class="inline"/>
<p class="inline text-md text-date dark:text-date_dark float-right">@article.Created.ToString("dd/MM/yy")</p>
</CardContent>
</Card>
</Column>
</ItemContent>
<Placeholder>
<Card>
<CardTitle Title="Loading..."/>
<CardContent>
In progres...
</CardContent>
</Card>
</Placeholder>
</Virtualize>
</Row>
The first results are correct. The problem starts with scrolling.
First loading, its ok:

1 break point scrool:

The next one is right away, I don’t have to do anything.

Scrolls on, 2 break point scroll:

Scrolls on, 3 brak point scroll:

Still ok, now takes the breakpoint off.
[02:07:20 ERR] An exception occurred while iterating over the results of a query for context type 'Stand.Plugins.Articles.Context'.
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.ConcurrencyDetector.EnterCriticalSection()
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
[02:07:20 WRN] Unhandled exception rendering component: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
at Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize`1.BuildRenderTree(RenderTreeBuilder builder)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
[02:07:20 ERR] Unhandled exception in circuit '7S3gOiTMV1OjU6v35dYZcg1i2Sb-MZExeXvGsHDWF2Q'.
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
at Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize`1.BuildRenderTree(RenderTreeBuilder builder)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
It turns out that everything is happening fast … I’m sitting for this half day today. I do not understand what’s going on… There is a documentation error or I don’t understand how to do this. Can I ask for help?
Expected Behavior
No response
Steps To Reproduce
No response
Exceptions (if any)
No response
.NET Version
.net 7 core rc1
Anything else?
No response
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 38 (20 by maintainers)
If you want to preserve already-rendered HTML, then
Virtualizeprobably isn’t the component you’re looking for. TheVirtualizecomponent was designed to be capable of handling quantities of items that would be too large to reasonably render all at once on the page without performance problems. If this capability isn’t a requirement for your use case, and if frequent communication with the server caused by scrolling is a legitimate concern, then it’s probably better to use a custom component.It’s necessary because
Virtualizeneeds to calculate how large the scrollable area should be, and it needs the total number of items to do so. If you can’t compute the actual total number of items, you can specify it as (number of currently-loaded items + 1) and you’ll get a sort of infinite scroll effect where the size of the scrollable area expands as more items are loaded.https://github.com/dotnet/aspnetcore/issues/28770
It seems you have a lot of ideas and concerns, and that’s awesome. However, it’s best that we keep comments on this issue related to this issue’s topic, which we’ve decided should be improving the
Virtualizedocs to make the “all item content should be the same size” requirement clearer.Going forward, would you please use the following process when making feature requests?
Thanks for your feedback and enthusiasm!
@Alerinos That seems like a reasonable suggestion. We currently have https://github.com/dotnet/aspnetcore/issues/10522 tracking the general idea of throttling/debouncing events, but the
Virtualizecase might require special attention. It doesn’t look like we have an issue for throttlingVirtualizespecifically, so feel free to open a separate issue for it 🙂 Thanks!@Alerinos, we have an issue tracking that as well: https://github.com/dotnet/aspnetcore/issues/28821
Yeah, unfortunately
NavigationManagerdoes this and currently there isn’t API to disable that behavior. But you can work around it today by doing something like this: https://github.com/dotnet/aspnetcore/issues/40190#issuecomment-1203857906Also, regarding:
The docs do state:
However, the phrase “content item” isn’t really defined in that document, and it’s not made explicit elsewhere that
<Placeholder>content is included in the “all items must have identical height” constraint. So, I agree the docs can be improved.Since there isn’t a bug in the framework, let’s use this issue to track improving the wording of the docs to make the placeholder content requirements more obvious.
@Alerinos sorry, but it’s hard to tell what the issue is in that video. What’s going wrong, exactly?
This is something we don’t support yet, but there is an issue tracking it: https://github.com/dotnet/aspnetcore/issues/26943
@Alerinos, could you apply that same styling to the
<Placeholder>as well? For example:@mkArtakMSFT https://github.com/Alerinos/Virtualize-bug