havit / Havit.Blazor

Free Bootstrap 5 components for ASP.NET Blazor + optional enterprise-level stack for Blazor development (gRPC code-first, layered architecture, localization, auth, ...)
https://havit.blazor.eu
MIT License
468 stars 65 forks source link

[HxGrid] NullReferenceException in StartDataProviderInProgress() #855

Closed bholbrook closed 4 weeks ago

bholbrook commented 1 month ago

I'd like to preface this with I don't know exactly how/why this is occurring, just that it is. I've tried to reproduce this issue in demo code but can't produce anything that replicates exactly what's going on in my application such that I can share it here...

This is the error I'm seeing:

Havit.Blazor.Components.Web.Bootstrap.HxGrid`1.<StartDataProviderInProgress>g__HandleTimerElapsedAsync|251_1()
   at Havit.Blazor.Components.Web.Bootstrap.HxGrid`1.<StartDataProviderInProgress>b__251_0(Object sender, ElapsedEventArgs e)
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_1(Object state)
   at System.Threading.QueueUserWorkItemCallback.Execute()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()

Something between 4.6.9 and 4.6.10 in the HxGrid component has changed such that a NullReferenceException is occurring on load of my components.

Comparing the two versions here: https://github.com/havit/Havit.Blazor/compare/v4.6.9...v4.6.10#diff-71ac77db351a747929499dedb1011e5171aee7a5b9e921e09de44a7e098c65fa My immediate guess is that there's not sufficient null checking in the StartDataProviderInProgress function. The HandleTimerElapsedAsync function disposes the _dataProviderInProgressDelayTimer and then sets it to null, but these lines:

                _dataProviderInProgressDelayTimer = new System.Timers.Timer(ProgressIndicatorDelayEffective);
                _dataProviderInProgressDelayTimer.AutoReset = false; // run once
#pragma warning disable VSTHRD101 // Avoid unsupported async delegates
                _dataProviderInProgressDelayTimer.Elapsed += async (sender, e) => await HandleTimerElapsedAsync();
#pragma warning restore VSTHRD101 // Avoid unsupported async delegates
                _dataProviderInProgressDelayTimer.Start();

Looks to me like the .Start() call could fail if null.

What do people think? Is something simple that might be fixable? Does my reasoning make sense here? Is this enough information to handle possible nulls in this block?

hakenr commented 1 month ago

@bholbrook Thanks for reporting the issue.

Although we do not have any other such reports yet, it seems (looking at the call stack you provided) that the exception comes from line 830: https://github.com/havit/Havit.Blazor/blob/2a1076ee71ae858f7e3f106ac7770f26553fd32c/Havit.Blazor.Components.Web.Bootstrap/Grids/HxGrid.razor.cs#L823-L832

I'll try to add a null-guard condition for this part and publish a new pre-release. Please let me know if it helps. (The _dataProviderInProgressDelayTimer is most likely set to null from Dispose() or maybe another HandleTimerElapsedAsync() running simultaneously.)

hakenr commented 1 month ago

@bholbrook release 4.6.11-pre1

hakenr commented 1 month ago

(We might also move the timer-disposing logic to the start of HandleTimerElapsedAsync to precede the await there.)

bholbrook commented 1 month ago

I've just updated to the pre-release 4.6.11-pre1 and I'm no longer experiencing the issue!