New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server Side System.IndexOutOfRangeException #1762

Closed
cyberjaws opened this Issue Dec 12, 2018 · 5 comments

Comments

Projects
None yet
2 participants
@cyberjaws
Copy link

cyberjaws commented Dec 12, 2018

System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.Generic.Dictionary2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) at Microsoft.AspNetCore.Blazor.Rendering.Renderer.AssignEventHandlerId(RenderTreeFrame& frame) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForAttributeFrame(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendAttributeDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForFramesWithSameSequence(DiffContext& diffContext, Int32 oldFrameIndex, Int32 newFrameIndex) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange1 oldTree, ArrayRange1 newTree) at Microsoft.AspNetCore.Blazor.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment) at Microsoft.AspNetCore.Blazor.Rendering.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry) at Microsoft.AspNetCore.Blazor.Rendering.Renderer.ProcessRenderQueue() at Microsoft.AspNetCore.Blazor.Rendering.Renderer.AddToRenderQueue(Int32 componentId, RenderFragment renderFragment) at Microsoft.AspNetCore.Blazor.Server.Circuits.CircuitSynchronizationContext.ExecuteSynchronously(TaskCompletionSource1 completion, SendOrPostCallback d, Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Blazor.Server.Circuits.CircuitSynchronizationContext.ExecuteBackground(WorkItem item)
Exception thrown: 'MessagePack.Internal.MessagePackDynamicObjectResolverException' in MessagePack.dll
Exception thrown: 'System.TypeInitializationException' in MessagePack.dll
Exception thrown: 'System.TypeInitializationException' in MessagePack.dll
Exception thrown: 'System.TypeInitializationException' in MessagePack.dll
Exception thrown: 'System.TypeInitializationException' in Microsoft.AspNetCore.SignalR.Protocols.MessagePack.dll
Exception thrown: 'MessagePack.Internal.MessagePackDynamicObjectResolverException' in MessagePack.dll
Exception thrown: 'System.IndexOutOfRangeException' in System.Private.CoreLib.dll
Exception thrown: 'System.IndexOutOfRangeException' in System.Private.CoreLib.dll
Microsoft.AspNetCore.Blazor.Server.BlazorHub:Warning: Unhandled Server-Side exception

@cyberjaws

This comment has been minimized.

Copy link

cyberjaws commented Dec 12, 2018

I am trying to recreate, however it seems to happen at random.
Here's the pseudo code for what I am doing in the @functions section

ArrayOfThings[] {get; set;} = new [] {}; // size 0
private async void LoadSomeStuffAsync() {
       ArrayOfThings[] = await MakeExternalCallAsync();
       StateHasChanged();
}

Render Code:

@for (int index = 0; index < ArrayOfThings.Length; index++)
            {
                var i = index;
                // Is there a bug in BLAZOR? Is it that this collection is getting modified from another thread
                // which causes an index out of bounds exception on the server?
                var thing = ArrayOfThings[i];
                <tr>
                    <td>@(i + 1)</td>
                    <td><input type="text" class="form-control" bind="@thing.Name" /></td>
                    <td><Button class="btn btn-secondary" onclick="@{() => DeleteThing(thing)}"><span class="oi oi-x"></span></Button></td>
                </tr>
            }
@AndreasTruetschel

This comment has been minimized.

Copy link

AndreasTruetschel commented Dec 13, 2018

Are ClientState.ArrayOfThings.Length in the for loop and ArrayOfThings[i] pointing to the same array, or is this just a typo?
How do you call the LoadSomeStuffAsync method?

To be sure to access the exactly same array instance when requesting the array length and getting the element at the respective index, you could copy the array reference to a new variable before the loop and access the array only through this variable. You have to ensure not to modify the content of the array to be on the safe side regarding threading issues here.

@cyberjaws

This comment has been minimized.

Copy link

cyberjaws commented Dec 13, 2018

Thanks for the consult. Indeed a typo, updated in the earlier post.
The LoadSomeStuffAsync is called from a button on the same form.

I will try the following:

@if (ArrayOfThings.Length > 0)
            {
                var things = ArrayOfThings;
                @for(int index = 0;index < things.Length;index++)
                ...

Is there a better way to declare the array inline before the loop (in this case, without the @if statement)?
Is there a way to marshall the result of the async call back to the "UI thread" (Is there such a thing in this context)?

@cyberjaws

This comment has been minimized.

Copy link

cyberjaws commented Dec 13, 2018

It was my understanding that the await would get marshalled back, however that is not a valid assumption. No worries!
Keep up the good work everyone, can't wait to get this into production. This is a game changer.

@cyberjaws cyberjaws closed this Dec 13, 2018

@AndreasTruetschel

This comment has been minimized.

Copy link

AndreasTruetschel commented Dec 13, 2018

Is there a better way to declare the array inline before the loop (in this case, without the @if statement)?

You can define a C# scope just before the for loop like:


@{
   var things = ArrayOfThings;
}
@for(int index = 0; index < things .Length; index++)
{ }

Is there a way to marshall the result of the async call back to the "UI thread" (Is there such a thing in this context)?

To may understanding, there is actually just a single thread running your code (if client side blazor is used) because wasm cannot handle multithreading as of the current version.
There is no SynchronizationContext installed by default, so there is no UI-thread queue, the continuation can be scheduled to.
This is also true for the server side version, where any thread pool thread may process the continuation.
@SteveSandersonMS Is this correct?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment