Files
fs-onboarding/.github/prompts/plan-prospectPerformance.prompt.md
T

5.2 KiB

Plan: Prospect list performance overhaul

The current bottleneck is not just data volume; it is the combination of loading every prospect and every nested interaction at once, then rendering all cards in the browser. In the current flow, each page loads the full list from ProspectService.GetProspectsAsync, includes all Images and Interactions plus each User, and then ProspectGrid renders every card through Repeater without virtualization. With hundreds of records, the UI does repeated LINQ work per card and hits a lot of DOM work in one render.

Use a two-part fix: reduce the server-side payload and virtualize the client-side rendering. The best gains will come from paginating or virtualizing the list, then trimming unnecessary nested load and repeated per-row calculations.

Steps

  1. Profile the bottleneck and cap the visible list size

    • Confirm the prospect pages are loading all records at once because GetProspectsAsync currently does ToListAsync() without paging or limit.
    • Add a page-size/virtualized flow so only the visible slice is fetched and rendered.
    • This change must happen in the page and service layer before any UI refactor.
  2. Reduce EF payload in ProspectService.GetProspectsAsync

    • Update FoodsharingSiegen.Server/Data/Service/ProspectService.cs so it accepts pagination arguments (skip, take, optional filters) instead of returning the full unbounded list.
    • Keep the query as AsNoTracking() but avoid eager-loading all Images and every User for every prospect when the page only needs a summary view.
    • For the list pages, load only the subset needed for the grid and defer expensive image/user data until a detail action opens.
    • Filter and sort should happen server-side when possible to avoid moving hundreds of full entities into memory and then filtering again in the browser.
  3. Replace the full list render with virtualization

    • Convert the repeated list in FoodsharingSiegen.Server/Controls/ProspectGrid.razor to a virtualized list or paged container, rather than rendering a full <Repeater Items="@Prospects"> for all rows.
    • Keep the visible cards only; the hidden rows should not be in the DOM.
    • This removes the biggest DOM cost on pages with hundreds of entries.
  4. Remove repeated per-row computation in the card components

    • In FoodsharingSiegen.Server/Controls/InteractionRow.razor.cs, the properties Interactions, Done, Alert, NotNeeded recalculate on every render from a list of nested items. With hundreds of cards this becomes expensive.
    • Replace repeated Where() chains with precomputed values from the loaded model or cached values at the prospect object level.
    • In FoodsharingSiegen.Contracts/Entity/Prospect.cs, avoid deriving Complete from Interactions.Any() on every render when a summary value is already known in the data model.
    • Keep the card logic free of repeated ToList() and Any() chains during render.
  5. Tighten data shaping for each list page

    • The onboarding, verification, archive, and done pages all load the same full set of entities and then apply client-side filters with ApplyFilter and ApplySort in FoodsharingSiegen.Shared/Helper/FilterHelper.cs.
    • Move the expensive filtering/sorting to SQL or at least to the server-side query layer; reduce the list to a smaller result set before it reaches the UI.
    • Keep ProspectList.ApplyFilter only as a fallback for small local slices, not as the primary path for large lists.
  6. Verify the improvement with focused checks

    • Measure page load time before and after with a realistic dataset of several hundred prospects.
    • Validate that the list stays responsive during scrolling and interaction changes.
    • Run the relevant .NET tests and render-check the prospect pages manually.

Relevant files

  • FoodsharingSiegen.Server/Data/Service/ProspectService.cs — main query and database fetch point.
  • FoodsharingSiegen.Server/Controls/ProspectGrid.razor — full-list rendering path.
  • FoodsharingSiegen.Server/Controls/ProspectContainer.razor — per-card DOM and nested component rendering.
  • FoodsharingSiegen.Server/Controls/InteractionRow.razor.cs — repeated LINQ work in each row.
  • FoodsharingSiegen.Contracts/Entity/Prospect.cs — computed values that trigger repeated work.
  • FoodsharingSiegen.Shared/Helper/FilterHelper.cs — client-side list filtering/sorting on large sets.
  • FoodsharingSiegen.Server/Pages/Prospects.razor, FoodsharingSiegen.Server/Pages/ProspectsAll.razor, FoodsharingSiegen.Server/Pages/ProspectsVerify.razor, FoodsharingSiegen.Server/Pages/ProspectsDone.razor — list page orchestration and data flow.

Key decision

The highest-value fix is to stop returning and rendering all prospects at once. A virtualized/paged list with a trimmed query shape will provide the biggest speedup and should be the first implementation target.

Optional follow-up improvements

  • Precompute a lightweight summary model for the grid instead of the full Prospect entity.
  • Add a server-side count query to support proper paging without loading every record.
  • Consider splitting the list into “summary” and “detail” data so the main dashboard never loads full nested relationship graphs by default.