How to programmatically update rows in Datagrid

I use the data RadzenGrid component.
Works great.
I also use SQL Server Service Broker to get notified when something in my table has changed. More info: https://github.com/christiandelbianco/blazor-notification-db-record-change

I use an IQueryable to load the data:

<RadzenGrid @ref="grid0" AllowFiltering="true" AllowPaging="true" AllowSorting="true" FilterCaseSensitivity="FilterCaseSensitivity.CaseInsensitive"
                            Data="@getClients" TItem="MDGR.Models.Entities.Master.Client">
        IEnumerable<MDGR.Models.Entities.Master.Client> _getClients;
        protected IEnumerable<MDGR.Models.Entities.Master.Client> getClients
        {
            get
            {
                return _getClients;
            }
            set
            {
                if (!object.Equals(_getClients, value))
                {
                    _getClients = value;
                    InvokeAsync(() => { StateHasChanged(); });
                }
            }
        }

        public override async System.Threading.Tasks.Task Load()
        {
            var result = await ClientService.GetAll(new Query { OrderBy = "Name" });
            getClients = result;
        }

        public async Task<IQueryable<Client>> GetAll(Query query = null)
        {
            var items = _context.Clients.AsQueryable();
            ...
        }

When the client table is changed, for example by another user or a WebJob the Service Broker broadcasts it and my ClientTableChanged event is triggered.
At this stage I know if a row is inserted, updated or deleted.
Now I can call await InvokeAsync(grid0.Reload); to let the grid fetch new data from the database. That isn't necessary because I just need to update the rows in the datagrid.

On an insert, it is fine to re-fetch the data from the database.
On an update, I receive the updated record so I should be able to update the row in the datagrid, without getting back to the database.
Also when the record is changed outside the datagrid and I call grid0.Reload() the changes are not reflected. I need to refresh the whole page to see the changes.
On a delete, I would like to mark the deleted row in the datagrid and disable the row and give it a different color, making it clear to the user somebody else has removed that item.

public override async void ClientTableChanged(object sender,
    TableChangeEventArgs<Models.Entities.Master.Client> args)
{
    
    if (args.ChangeType == ChangeType.Insert)
    {
        // it is OK to re-fetch the data:
        await InvokeAsync(grid0.Reload);
    }

    if (args.ChangeType == ChangeType.Update)
    {
        // Update list without going back to the database.
        // TODO: This doesn't seems to be the proper workflow
        using (var enumerator = grid0.Data.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                var item = enumerator.Current;
                if (item.Id == args.NewValue.Id)
                {
                    // Raises an error: The current thread is not associated with the Dispatcher.
                    await grid0.EditRow(item);
                    
                    // TODO: How to update the row
                    await grid0.UpdateRow(args.NewValue);
                    
                    // TODO: How to color the row:
                }
            }
        }
    }

    if (args.ChangeType == ChangeType.Delete)
    {
        // TODO: Color the row red and disable, using the Id: args.NewValue.Id 
    }

    // Triggers a new fetch from the database, which I want to prevent:
    // await InvokeAsync(grid0.Reload);
    await InvokeAsync(StateHasChanged);
}

What I'm basically looking for it getting direct access to the rows in the datagrid.

I found a solution, which works for me.
I'll share it here.

I first start with converting the IQueryable to an IList, effectively loading all records at once.
I now have a snapshot of my database table.
Next I update the list (add, update & delete) in my ClientTableChanged event so I don't need to go back to the database. After calling Grid.Reload() the grid is updated.

For reference:

        public override async System.Threading.Tasks.Task Load()
        {
            var result = await ClientService.GetAll(new Query { OrderBy = "Name" });
            // Load all and save to list, manage add, update and delete via Service Broker:
            getClients = result.ToList();
        }
        public override async void ClientTableChanged(object sender,
            TableChangeEventArgs<Models.Entities.Master.Client> args)
        {
            // Update list without going back to the database.
            if (args.ChangeType == ChangeType.Insert)
            {
                _getClients.Add(args.NewValue);
            }

            var oldClient = _getClients.FirstOrDefault(x => x.Id == args.NewValue.Id);
            if (oldClient != null)
            {
                var index = _getClients.IndexOf(oldClient);
                if (index > 0)
                {
                    if (args.ChangeType == ChangeType.Update)
                    {
                        _getClients[index] = args.NewValue;
                    }
                    if (args.ChangeType == ChangeType.Delete)
                    {
                        _getClients.RemoveAt(index);
                    }
                }
            }

            // Update the grid:
            await InvokeAsync(ClientsGrid.Reload);
            // Update the page:
            await InvokeAsync(StateHasChanged);
        }
2 Likes