LostBeard / SpawnDev.BlazorJS

Full Blazor WebAssembly and Javascript Interop with multithreading via WebWorkers
https://blazorjs.spawndev.com
MIT License
78 stars 6 forks source link

StackOverflowException when calling my async method #8

Closed ChaseFlorell closed 10 months ago

ChaseFlorell commented 10 months ago

My Blazor WASM app is running on net7.0, and using this package

<PackageVersion Include="SpawnDev.BlazorJS.WebWorkers" Version="2.2.0" />

I'm getting the below exception when calling the following async method from within my ViewModel

public async Task DownloadFile(Product product)
{
    using var _ = Busy();
    using var webWorker = await _webWorkerService.GetWebWorker() ?? throw new InvalidOperationException("Unable to find web worker");
    var exportService = webWorker.GetService<IProductExportService>();
    var result = await exportService.Export(product).ConfigureAwait(false);
    await _jsRuntime.InvokeVoidAsync("BlazorDownloadFile", result.Name, Convert.ToBase64String(result.Data)).ConfigureAwait(true);
}
image

I think I have my Program.cs setup correctly, so I'm at a loss as to what to do.

image
LostBeard commented 10 months ago

Is it the below line that is failing? (The line that calls the IProductExportService.Export service method on the web worker.)

var result = await exportService.Export(product).ConfigureAwait(false);

I don't see a service registered with the interface IProductExportService anywhere in your Program.cs.

Can you show the return type class for IProductExportService.Export please?

ChaseFlorell commented 10 months ago
image image
LostBeard commented 10 months ago

Where are you registering the service IProductExportService?

ChaseFlorell commented 10 months ago

sorry, all of my dependencies are being registered in DryIoC I should have mentioned before, I'm able to execute everything successfully without the web worker bits, it's just locking the UI while it processes in memory (about 30 seconds).

image

All of that setup happens in the Program.cs you see above

image
LostBeard commented 10 months ago

I have removed the method "HasIJSInProcessObjectReferenceConstructor" that I see in your error call stack. Try upgrading to 2.2.1 and re-test please.

If you still have the issue... what is the error (it should have changed if not fixed)?

Is the error before or after the WebWorker loads? You should see some web worker output in the browser console indicating it loaded if it does laod. Ex. dotnet Loaded 9.49 MB resources.

Can you create a test method in IProductExportService that takes no arguments and returns a bool or any simple value type?

ChaseFlorell commented 10 months ago

Interesting, I get a similar SOE with the update, and when I change the method to just return a primitive, I get this output.

image image image
LostBeard commented 10 months ago

The "Service not registered" error is almost certainly due to the use of DryIoc (new to me.) To make WebWorkers compatible I will have to create a test project that uses DryIoc and tinker a bit. Then we can go from there.

LostBeard commented 10 months ago

I just tested a very simple WebWorkers project with DryIoc and it worked without issue. So that does not appear to be the problem unfortunately.

Where is that section called that registers those services (// Services)? If they are not registered before BlazorJSRunAsync is called they will not be available in a WebWorker. I assume one of the RegisterModule calls does it but I may be wrong and I don't have that method with the DryIoc Nuget packages I am using.

I noticed in your last post your console output that showed the dotnet Loaded 18.93 MB resources from your main window and your web worker used different MB amounts (18.93 MB and 20.91 MB.) Slight chance your web worker and window loaded a different version of your app due to page caching or PWA code.

Below is the basic DryIoc project Program.cs code I used for my test.

using DryIoc;
using DryIoc.Microsoft.DependencyInjection;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using SpawnDev.BlazorJS;
using SpawnDev.BlazorJS.WebWorkers;

namespace DrylocContainerTest
{
    public interface ITestService
    {
        Task<bool> TestMethod();
    }

    public class TestService : ITestService
    {
        public Task<bool> TestMethod() => Task.FromResult(true);
    }

    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("#app");
            builder.RootComponents.Add<HeadOutlet>("head::after");

            builder.Services.AddBlazorJSRuntime();
            builder.Services.AddWebWorkerService();

            var useDryIoc = true;
            Console.WriteLine($"useDryIoc: {useDryIoc}");
            if (useDryIoc)
            {
                var container = new Container(Rules.Default.With(FactoryMethod.ConstructorWithResolvableArguments, propertiesAndFields: PropertiesAndFields.Auto).WithTrackingDisposableTransients());
                var diFactory = new DryIocServiceProviderFactory(container, RegistrySharing.Share);
                builder.ConfigureContainer(diFactory);
                container.Register<ITestService, TestService>(Reuse.Singleton);
            }
            else
            {
                builder.Services.AddSingleton<ITestService, TestService>();
            }

            var host = builder.Build();
            // make "_testWorker()" callable from the browser console
            BlazorJSRuntime.JS.Set("_testWorker", new ActionCallback(async () =>
            {
                var webWorkerService = host.Services.GetRequiredService<WebWorkerService>();
                using var worker = await webWorkerService.GetWebWorker();
                var workerTestService = worker.GetService<ITestService>();
                var ret = await workerTestService.TestMethod();
                BlazorJSRuntime.JS.Log(ret);
            }));
            await host.BlazorJSRunAsync();
        }
    }
}
LostBeard commented 10 months ago

Sorry I have not been able to diagnose the issue you are having. I'm sure I can solve it if you can create a minimal project that can reproduce the issue.

ChaseFlorell commented 10 months ago

I am out of service for the weekend starting pretty soon, but I will try changing the order of operations on my container and see if that makes a difference. I will follow up next week, thank you for your help!


From: Todd T @.> Sent: Saturday, August 12, 2023 8:00:04 AM To: LostBeard/SpawnDev.BlazorJS @.> Cc: Chase Florell @.>; Author @.> Subject: Re: [LostBeard/SpawnDev.BlazorJS] StackOverflowException when calling my async method (Issue #8)

Sorry I have not been able to diagnose the issue you are having. I'm sure I can solve it if you can create a minimal project that can reproduce the issue.

— Reply to this email directly, view it on GitHubhttps://github.com/LostBeard/SpawnDev.BlazorJS/issues/8#issuecomment-1675952953, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AADTESRBPQAFQCSKHYWAFTTXU6K7JANCNFSM6AAAAAA3MA5ESE. You are receiving this because you authored the thread.Message ID: @.***>

LostBeard commented 10 months ago

Cannot reproduce.