Grid Transaction API
Efficient delta updates using manual transaction APIs. Perfect for high-performance scenarios with millions of rows.
Manual Add Transaction
Use AddRowsAsync() to add rows without refreshing the entire grid.
When to use AddRowsAsync()
- Adding new rows from API responses
- Real-time data feeds (WebSocket, SignalR)
- Optimistic UI updates after create operations
- When you control the data source manually (not ObservableCollection)
Loading...
Total: 10 rows (Added via transaction API - no full refresh)
View Code
@code {
private Grid<Order> addGrid;
private List<Order> orders = new();
private async Task AddSingleRow()
{
var newOrder = new Order { /* ... */ };
// Add to data source
orders.Add(newOrder);
// Apply transaction - only new row is rendered!
await addGrid.AddRowsAsync(newOrder);
}
private async Task AddMultipleRows()
{
var newOrders = GenerateOrders(5);
orders.AddRange(newOrders);
// Batch add - all 5 rows added in single transaction
await addGrid.AddRowsAsync(newOrders.ToArray());
}
}Manual Update Transaction
Use UpdateRowsAsync() to update specific rows efficiently.
When to use UpdateRowsAsync()
- Updating rows after API PATCH/PUT responses
- Real-time updates from WebSocket messages
- Optimistic UI updates after edit operations
- Batch status updates (e.g., mark as "Shipped")
Loading...
11 pending orders
View Code
@code {
private Grid<Order> updateGrid;
private List<Order> orders = new();
private async Task UpdateRandomRow()
{
if (updateOrders.Count == 0) return;
var statuses = new[] { OrderStatus.Pending, OrderStatus.Shipped, OrderStatus.Delivered };
var order = updateOrders[Random.Shared.Next(updateOrders.Count)];
order.Amount = Math.Round((decimal)(Random.Shared.NextDouble() * 10000 + 100), 2);
order.Status = statuses[Random.Shared.Next(statuses.Length)];
await updateGrid.UpdateRowsAsync(order);
}
private async Task UpdateMultipleRows()
{
var statuses = new[] { OrderStatus.Pending, OrderStatus.Shipped, OrderStatus.Delivered };
var ordersToUpdate = updateOrders.OrderBy(x => Random.Shared.Next()).Take(5).ToArray();
foreach (var order in ordersToUpdate)
{
order.Amount = Math.Round((decimal)(Random.Shared.NextDouble() * 10000 + 100), 2);
order.Status = statuses[Random.Shared.Next(statuses.Length)];
}
await updateGrid.UpdateRowsAsync(ordersToUpdate);
}
private async Task UpdateAllPending()
{
var pendingOrders = updateOrders.Where(o => o.Status == OrderStatus.Pending).ToArray();
foreach (var order in pendingOrders)
order.Status = OrderStatus.Shipped;
await updateGrid.UpdateRowsAsync(pendingOrders);
}
}Manual Remove Transaction
Use RemoveRowsAsync() to remove rows without full grid refresh.
When to use RemoveRowsAsync()
- After successful DELETE API calls
- Removing expired/invalid rows
- Bulk delete operations
- When you manually manage the data list (not ObservableCollection)
Loading...
30 rows remaining
View Code
@code {
private Grid<Order> removeGrid;
private List<Order> orders = new();
private IReadOnlyCollection<Order> selectedOrders = Array.Empty<Order>();
private async Task RemoveSelected()
{
// Remove from data source
orders.RemoveAll(o => selectedOrders.Contains(o));
// Apply transaction - only removed rows affected!
await removeGrid.RemoveRowsAsync(selectedOrders.ToArray());
selectedOrders = Array.Empty<Order>();
}
private async Task RemoveDelivered()
{
var delivered = orders.Where(o => o.Status == "Delivered").ToArray();
// Remove from list
orders.RemoveAll(o => delivered.Contains(o));
// Batch remove - all removed in one transaction
await removeGrid.RemoveRowsAsync(delivered);
}
}Combined Transaction Operations
Perform multiple operations (add, update, remove) simultaneously for complex scenarios.
Real-world Combined Operations
- Order Processing: Ship pending (update), archive delivered (remove), add new orders
- Inventory Sync: Add new products, update prices, remove discontinued items
- User Management: Activate new users, update roles, remove inactive accounts
- All in one efficient operation - Single grid transaction, minimal DOM updates
Loading...
Total: 20 orders
Pending: 8 | Shipped: 4 | Delivered: 3
View Code
@code {
private Grid<Order> combinedGrid;
private List<Order> orders = new();
private async Task ProcessOrders()
{
// 1. Ship pending orders (UPDATE)
var toShip = orders.Where(o => o.Status == "Pending").ToArray();
foreach (var order in toShip)
order.Status = "Shipped";
// 2. Archive delivered orders (REMOVE)
var toArchive = orders.Where(o => o.Status == "Delivered").ToArray();
orders.RemoveAll(o => toArchive.Contains(o));
// 3. Add new incoming orders (ADD)
var newOrders = GenerateOrders(3);
orders.AddRange(newOrders);
// Execute all operations in optimal sequence
// Grid batches them into a single efficient transaction!
await combinedGrid.UpdateRowsAsync(toShip);
await combinedGrid.RemoveRowsAsync(toArchive);
await combinedGrid.AddRowsAsync(newOrders.ToArray());
}
}Performance Comparison
Compare full refresh vs. transaction API performance
❌ Full Refresh (Slow)
// Remove 1 item from 10,000 rows
orders.Remove(order);
orders = new List<Order>(orders); // Trigger re-render
// Result: ALL 9,999 rows re-rendered 🐌✅ Transaction API (Fast)
// Remove 1 item from 10,000 rows
orders.Remove(order);
await grid.RemoveRowsAsync(order);
// Result: Only 1 row removed from DOM ⚡Best Practices
- Use ObservableCollection when you own the data source (simplest)
- Use Transaction APIs when integrating with external data sources (APIs, SignalR)
- Batch operations when possible (e.g., AddRowsAsync([...]) instead of multiple AddRowsAsync())
- Avoid full refresh when only a few rows changed