diff --git a/NuGet.config b/NuGet.config
index 5d294cb9826e..39f0f03717d3 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -4,23 +4,10 @@
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
@@ -38,23 +25,10 @@
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props
index 4fde644ed567..cf29666e3f5a 100644
--- a/eng/Baseline.Designer.props
+++ b/eng/Baseline.Designer.props
@@ -2,28 +2,28 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
-
-
+
+
+
@@ -34,120 +34,120 @@
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
@@ -155,114 +155,114 @@
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
-
-
-
+
+
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
-
+
+
@@ -270,7 +270,7 @@
- 6.0.32
+ 6.0.33
@@ -278,50 +278,50 @@
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
-
+
+
@@ -331,8 +331,8 @@
-
-
+
+
@@ -340,8 +340,8 @@
-
-
+
+
@@ -352,58 +352,58 @@
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
@@ -411,71 +411,71 @@
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
@@ -491,195 +491,195 @@
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
-
-
+
+
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
-
-
+
+
-
-
+
+
-
-
+
+
- 6.0.32
+ 6.0.33
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
-
-
-
+
+
+
+
- 6.0.32
+ 6.0.33
@@ -688,69 +688,69 @@
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
-
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
@@ -772,7 +772,7 @@
- 6.0.32
+ 6.0.33
@@ -791,7 +791,7 @@
- 6.0.32
+ 6.0.33
@@ -807,46 +807,46 @@
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
-
-
+
+
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
@@ -856,7 +856,7 @@
- 6.0.32
+ 6.0.33
@@ -865,73 +865,73 @@
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
-
+
-
+
-
+
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
@@ -960,11 +960,11 @@
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
@@ -982,13 +982,13 @@
- 6.0.32
+ 6.0.33
- 6.0.32
+ 6.0.33
-
+
\ No newline at end of file
diff --git a/eng/Baseline.xml b/eng/Baseline.xml
index f4833bd7681f..df5c7d258e16 100644
--- a/eng/Baseline.xml
+++ b/eng/Baseline.xml
@@ -4,111 +4,111 @@ This file contains a list of all the packages and their versions which were rele
Update this list when preparing for a new patch.
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 8e6d923f4edb..3b9cdcc8a7cd 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -9,37 +9,37 @@
-->
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53f
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53f
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53f
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53f
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53f
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53f
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53f
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-efcore
- d938643248c7390b295b44237eb64520ac69fd53
+ 73adbdc34d00e8fc71cb835171ee5ba73cc9e53fhttps://github.com/dotnet/runtime
@@ -177,9 +177,9 @@
https://github.com/dotnet/runtime4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- e77011b31a3e5c47d931248a64b47f9b2d47853d
+ 6c636980f730a30c3f5352cff80ce035ae53f016https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
@@ -245,33 +245,33 @@
https://github.com/dotnet/runtime4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- e77011b31a3e5c47d931248a64b47f9b2d47853d
+ 6c636980f730a30c3f5352cff80ce035ae53f016
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- e77011b31a3e5c47d931248a64b47f9b2d47853d
+ 6c636980f730a30c3f5352cff80ce035ae53f016
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- e77011b31a3e5c47d931248a64b47f9b2d47853d
+ 6c636980f730a30c3f5352cff80ce035ae53f016
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- e77011b31a3e5c47d931248a64b47f9b2d47853d
+ 6c636980f730a30c3f5352cff80ce035ae53f016
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- e77011b31a3e5c47d931248a64b47f9b2d47853d
+ 6c636980f730a30c3f5352cff80ce035ae53f016
-
+ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- e77011b31a3e5c47d931248a64b47f9b2d47853d
+ 6c636980f730a30c3f5352cff80ce035ae53f016
diff --git a/eng/Versions.props b/eng/Versions.props
index c5d8576c496a..c3e507fad832 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -9,7 +9,7 @@
6034
- false
+ true
@@ -63,12 +63,12 @@
6.0.0
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32-servicing.24314.7
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.33-servicing.24366.106.0.06.0.16.0.0
@@ -103,7 +103,7 @@
6.0.06.0.06.0.0
- 6.0.32-servicing.24314.7
+ 6.0.33-servicing.24366.106.0.16.0.06.0.2
@@ -122,14 +122,14 @@
6.0.13
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32
- 6.0.32
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.33
+ 6.0.336.0.0-beta.24360.76.0.0-beta.24360.7
diff --git a/eng/common/templates-official/steps/get-delegation-sas.yml b/eng/common/templates-official/steps/get-delegation-sas.yml
index c0e8f91317f0..8fea69b62407 100644
--- a/eng/common/templates-official/steps/get-delegation-sas.yml
+++ b/eng/common/templates-official/steps/get-delegation-sas.yml
@@ -28,12 +28,17 @@ steps:
# Calculate the expiration of the SAS token and convert to UTC
$expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
- $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv
+ # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads
+ # of correlation payloads.
+ $sas = ""
+ do {
+ $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv
- if ($LASTEXITCODE -ne 0) {
- Write-Error "Failed to generate SAS token."
- exit 1
- }
+ if ($LASTEXITCODE -ne 0) {
+ Write-Error "Failed to generate SAS token."
+ exit 1
+ }
+ } while($sas.IndexOf('/') -ne -1)
if ('${{ parameters.base64Encode }}' -eq 'true') {
$sas = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($sas))
diff --git a/global.json b/global.json
index 681f316666d6..32f7d4180837 100644
--- a/global.json
+++ b/global.json
@@ -1,9 +1,9 @@
{
"sdk": {
- "version": "6.0.132"
+ "version": "6.0.133"
},
"tools": {
- "dotnet": "6.0.132",
+ "dotnet": "6.0.133",
"runtimes": {
"dotnet/x64": [
"2.1.30",
diff --git a/src/Components/Web/src/Virtualization/Virtualize.cs b/src/Components/Web/src/Virtualization/Virtualize.cs
index 1e729de7404f..cc38528412cf 100644
--- a/src/Components/Web/src/Virtualization/Virtualize.cs
+++ b/src/Components/Web/src/Virtualization/Virtualize.cs
@@ -29,6 +29,14 @@ public sealed class Virtualize : ComponentBase, IVirtualizeJsCallbacks, I
private int _visibleItemCapacity;
+ // If the client reports a viewport so large that it could show more than MaxItemCount items,
+ // we keep track of the "unused" capacity, which is the amount of blank space we want to leave
+ // at the bottom of the viewport (as a number of items). If we didn't leave this blank space,
+ // then the bottom spacer would always stay visible and the client would request more items in an
+ // infinite (but asynchronous) loop, as it would believe there are more items to render and
+ // enough space to render them into.
+ private int _unusedItemCapacity;
+
private int _itemCount;
private int _loadedItemsStartIndex;
@@ -240,18 +248,23 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
var itemsAfter = Math.Max(0, _itemCount - _visibleItemCapacity - _itemsBefore);
builder.OpenElement(6, "div");
- builder.AddAttribute(7, "style", GetSpacerStyle(itemsAfter));
+ builder.AddAttribute(7, "style", GetSpacerStyle(itemsAfter, _unusedItemCapacity));
builder.AddElementReferenceCapture(8, elementReference => _spacerAfter = elementReference);
builder.CloseElement();
}
+ private string GetSpacerStyle(int itemsInSpacer, int numItemsGapAbove)
+ => numItemsGapAbove == 0
+ ? GetSpacerStyle(itemsInSpacer)
+ : $"height: {(itemsInSpacer * _itemSize).ToString(CultureInfo.InvariantCulture)}px; transform: translateY({(numItemsGapAbove * _itemSize).ToString(CultureInfo.InvariantCulture)}px);";
+
private string GetSpacerStyle(int itemsInSpacer)
=> $"height: {(itemsInSpacer * _itemSize).ToString(CultureInfo.InvariantCulture)}px;";
void IVirtualizeJsCallbacks.OnBeforeSpacerVisible(float spacerSize, float spacerSeparation, float containerSize)
{
- CalcualteItemDistribution(spacerSize, spacerSeparation, containerSize, out var itemsBefore, out var visibleItemCapacity);
+ CalcualteItemDistribution(spacerSize, spacerSeparation, containerSize, out var itemsBefore, out var visibleItemCapacity, out var unusedItemCapacity);
// Since we know the before spacer is now visible, we absolutely have to slide the window up
// by at least one element. If we're not doing that, the previous item size info we had must
@@ -262,12 +275,12 @@ void IVirtualizeJsCallbacks.OnBeforeSpacerVisible(float spacerSize, float spacer
itemsBefore--;
}
- UpdateItemDistribution(itemsBefore, visibleItemCapacity);
+ UpdateItemDistribution(itemsBefore, visibleItemCapacity, unusedItemCapacity);
}
void IVirtualizeJsCallbacks.OnAfterSpacerVisible(float spacerSize, float spacerSeparation, float containerSize)
{
- CalcualteItemDistribution(spacerSize, spacerSeparation, containerSize, out var itemsAfter, out var visibleItemCapacity);
+ CalcualteItemDistribution(spacerSize, spacerSeparation, containerSize, out var itemsAfter, out var visibleItemCapacity, out var unusedItemCapacity);
var itemsBefore = Math.Max(0, _itemCount - itemsAfter - visibleItemCapacity);
@@ -280,7 +293,7 @@ void IVirtualizeJsCallbacks.OnAfterSpacerVisible(float spacerSize, float spacerS
itemsBefore++;
}
- UpdateItemDistribution(itemsBefore, visibleItemCapacity);
+ UpdateItemDistribution(itemsBefore, visibleItemCapacity, unusedItemCapacity);
}
private void CalcualteItemDistribution(
@@ -288,7 +301,8 @@ private void CalcualteItemDistribution(
float spacerSeparation,
float containerSize,
out int itemsInSpacer,
- out int visibleItemCapacity)
+ out int visibleItemCapacity,
+ out int unusedItemCapacity)
{
if (_lastRenderedItemCount > 0)
{
@@ -302,11 +316,21 @@ private void CalcualteItemDistribution(
_itemSize = ItemSize;
}
+ // This AppContext data exists as a stopgap for .NET 8 and earlier, since this is being added in a patch
+ // where we can't add new public API.
+ var maxItemCount = AppContext.GetData("Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.MaxItemCount") switch
+ {
+ int val => val, // In .NET 9, this will be Math.Min(val, MaxItemCount)
+ _ => 1000 // In .NET 9, this will be MaxItemCount
+ };
+
itemsInSpacer = Math.Max(0, (int)Math.Floor(spacerSize / _itemSize) - OverscanCount);
visibleItemCapacity = (int)Math.Ceiling(containerSize / _itemSize) + 2 * OverscanCount;
+ unusedItemCapacity = Math.Max(0, visibleItemCapacity - maxItemCount);
+ visibleItemCapacity -= unusedItemCapacity;
}
- private void UpdateItemDistribution(int itemsBefore, int visibleItemCapacity)
+ private void UpdateItemDistribution(int itemsBefore, int visibleItemCapacity, int unusedItemCapacity)
{
// If the itemcount just changed to a lower number, and we're already scrolled past the end of the new
// reduced set of items, clamp the scroll position to the new maximum
@@ -316,10 +340,11 @@ private void UpdateItemDistribution(int itemsBefore, int visibleItemCapacity)
}
// If anything about the offset changed, re-render
- if (itemsBefore != _itemsBefore || visibleItemCapacity != _visibleItemCapacity)
+ if (itemsBefore != _itemsBefore || visibleItemCapacity != _visibleItemCapacity || unusedItemCapacity != _unusedItemCapacity)
{
_itemsBefore = itemsBefore;
_visibleItemCapacity = visibleItemCapacity;
+ _unusedItemCapacity = unusedItemCapacity;
var refreshTask = RefreshDataCoreAsync(renderOnSuccess: true);
if (!refreshTask.IsCompleted)
diff --git a/src/Components/test/E2ETest/Tests/VirtualizationTest.cs b/src/Components/test/E2ETest/Tests/VirtualizationTest.cs
index d8640655c619..608f1297fb9e 100644
--- a/src/Components/test/E2ETest/Tests/VirtualizationTest.cs
+++ b/src/Components/test/E2ETest/Tests/VirtualizationTest.cs
@@ -32,6 +32,25 @@ protected override void InitializeAsyncCore()
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
}
+ [Fact]
+ public void CanLimitMaxItemsRendered()
+ {
+ Browser.MountTestComponent();
+
+ // Despite having a 600px tall scroll area and 30px high items (600/30=20),
+ // we only render 10 items due to the MaxItemCount setting
+ var scrollArea = Browser.Exists(By.Id("virtualize-scroll-area"));
+ var getItems = () => scrollArea.FindElements(By.ClassName("my-item"));
+ Browser.Equal(10, () => getItems().Count);
+ Browser.Equal("Id: 0; Name: Thing 0", () => getItems().First().Text);
+
+ // Scrolling still works and loads new data, though there's no guarantee about
+ // exactly how many items will show up at any one time
+ Browser.ExecuteJavaScript("document.getElementById('virtualize-scroll-area').scrollTop = 300;");
+ Browser.NotEqual("Id: 0; Name: Thing 0", () => getItems().First().Text);
+ Browser.True(() => getItems().Count > 3 && getItems().Count <= 10);
+ }
+
[Fact]
public void AlwaysFillsVisibleCapacity_Sync()
{
diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor
index 634a9b2ab72c..24165a012b81 100644
--- a/src/Components/test/testassets/BasicTestApp/Index.razor
+++ b/src/Components/test/testassets/BasicTestApp/Index.razor
@@ -97,6 +97,7 @@
+
diff --git a/src/Components/test/testassets/BasicTestApp/VirtualizationMaxItemCount.razor b/src/Components/test/testassets/BasicTestApp/VirtualizationMaxItemCount.razor
new file mode 100644
index 000000000000..f48305dde1c9
--- /dev/null
+++ b/src/Components/test/testassets/BasicTestApp/VirtualizationMaxItemCount.razor
@@ -0,0 +1,48 @@
+@implements IDisposable
+
+ MaxItemCount is a safeguard against the client reporting a giant viewport and causing the server to perform a
+ correspondingly giant data load and then tracking a lot of render state.
+
+
+
+ If MaxItemCount is exceeded (which it never should be for a well-behaved client), we don't offer any guarantees
+ that the behavior will be nice for the end user. We just guarantee to limit the .NET-side workload. As such this
+ E2E test deliberately does a bad thing of setting MaxItemCount to a low value for test purposes. Applications
+ should not do this.
+
+
+
+ @* In .NET 8 and earlier, the E2E test uses an AppContext.SetData call to set MaxItemCount *@
+ @* In .NET 9 onwards, it's a Virtualize component parameter *@
+
+
+ Id: @context.Id; Name: @context.Name
+
+
+
+
+@code {
+ protected override void OnInitialized()
+ {
+ // This relies on Xunit's default behavior of running tests in the same collection sequentially,
+ // not in parallel. From .NET 9 onwards this can be removed in favour of a Virtualize parameter.
+ AppDomain.CurrentDomain.SetData("Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.MaxItemCount", 10);
+ }
+
+ private async ValueTask> GetItems(ItemsProviderRequest request)
+ {
+ const int numThings = 100000;
+
+ await Task.Delay(100);
+ return new ItemsProviderResult(
+ Enumerable.Range(request.StartIndex, request.Count).Select(i => new MyThing(i, $"Thing {i}")),
+ numThings);
+ }
+
+ record MyThing(int Id, string Name);
+
+ public void Dispose()
+ {
+ AppDomain.CurrentDomain.SetData("Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize.MaxItemCount", null);
+ }
+}