Skip to content

JS interop can cause unhandled server-side exception if dotNetObjectId is unknown/disposed #11641

@ZergZe

Description

@ZergZe

@SteveSandersonMS hijacking the original post... see the comment below for a more concise statement about what we need to fix.


Describe the bug

A clear and concise description of what the bug is.
I need to process client event on server but after some timeout.
Scenario:

  1. user types in input
  2. user removes focus from input
  3. some timeout passes, if user do not return focus to input -> js code calls server method of DotNetObjectRef , created from an instance of server component (ComponentBase deriven class).

To Reproduce

Problem happens when user types something in input and click some navigation link.
In this case following happens:

  1. user types in input
  2. user clicks navigation link -> it removes focus from input
  3. previous component disposes (and disposes the DotNetObjectRef object)
  4. some timeout passes -> js code calls server method of disposed instance of server component (and disposed DotNetObjectRef object created from that disposed component)
  5. client application gets "There is no tracked object with id"... "Perhaps the DotNetObjectRef instance was already disposed." message and spots working.

Expected behavior

Since the DotNetObjectRef object and the target component are disposed operation can't be completed, but there is a way for me to ignore it, because i do not need it any more in this scenario.

Simplest project to reproduce

Counter2.razor

@page "/counter2"
@inherits SomeNameSpace.Counter2Base

<input @ref="@input" />
<span>@Text</span>

Counter2.razor.cs

using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;

namespace SomeNameSpace
{
    public partial class Counter2Base : ComponentBase, IDisposable
    {
        public ElementRef input;
        public string Text;

        [Inject] IJSRuntime JsRuntime { get; set; }
        [Inject] IComponentContext ComponentContext { get; set; }

        object objReg;
        object ObjReg {
            get {
                if (objReg == null)
                    objReg = DotNetObjectRef.Create(this);
                return objReg;
            }
        }

        protected override void OnAfterRender()
        {
            SubscribeEvents();
        }

        public async void SubscribeEvents()
        {
            await JsRuntime.InvokeAsync<object>(
                    "exampleJsFunctions.SubscribeEvents", input, ObjReg);
        }

        [JSInvokable]
        public void LostFocus(string text)
        {
            Text = text;
            StateHasChanged();
        }

        public void Dispose()
        {
            var disposableObjReg = objReg as IDisposable;
            if(disposableObjReg != null)
                disposableObjReg.Dispose();
        }
    }
}

exampleJsInterop.js

window.exampleJsFunctions = {
    SubscribeEvents: function (element, dotnetHelper) {
        element.addEventListener('blur', function (e) {
            window.setTimeout(function () {
                dotnetHelper.invokeMethodAsync('LostFocus', e.target.value);
            }, 1000);
        });
    },
};
  1. Navigate to "counter2" page
  2. Open console dev tools in browser to see the error message
  3. type something to the input and click some navigation link except "counter2" to leave this page
  4. error, app stops operating

Possible workaround?

What is the best strategy to workaround this?

  1. I can remove the objReg disposing code (the object created by DotNetObjectRef). But who will be releasing it in this case?

  2. I can create a static collection of that objReg objects, mark them with some "ReadyToDispose" flag instead of disposing, and dispose them after some safe timeout on server.

May be something also? Please suggest.

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    ✔️ Resolution: DuplicateResolved as a duplicate of another issuearea-blazorIncludes: Blazor, Razor ComponentsbugThis issue describes a behavior which is not expected - a bug.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions