Closed waleed-alharthi closed 6 months ago
Update: Added an interface to UnitOfWork, still getting the same error
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Cannot resolve scoped service 'RihalDotnetTemplateWasm.Client.Data.IUnitOfWork' from root provider.
System.Exception: Cannot resolve scoped service 'RihalDotnetTemplateWasm.Client.Data.IUnitOfWork' from root provider.
at SpawnDev.BlazorJS.WebWorkers.ServiceCallDispatcher.Call(MethodInfo methodInfo, Object[] args) in D:\users\tj\Projects\SpawnDev.BlazorJS\SpawnDev.BlazorJS\SpawnDev.BlazorJS.WebWorkers\ServiceCallDispatcher.cs:line 372
at SpawnDev.BlazorJS.Reflection.InterfaceCallDispatcher`1.<InvokeTaskVoid>d__10[[RihalDotnetTemplateWasm.Client.Services.ISyncService, RihalDotnetTemplateWasm.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() in D:\users\tj\Projects\SpawnDev.BlazorJS\SpawnDev.BlazorJS\SpawnDev.BlazorJS\Reflection\InterfaceCallDispatcher.cs:line 42
at RihalDotnetTemplateWasm.Client.Data.DynamicRepository.GetToListAsync(Type entityType, CancellationToken cancellationToken, Boolean doSync) in C:\Users\alhar\source\repos\rihal-dotnet-template-wasm\RihalDotnetTemplateWasm\Client\Data\DynamicRepository.cs:line 36
at RihalDotnetTemplateWasm.Client.Features.Models.ModelsTable.OnParametersSetAsync() in C:\Users\alhar\source\repos\rihal-dotnet-template-wasm\RihalDotnetTemplateWasm\Client\Features\Models\ModelsTable.razor:line 72
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
Maybe all services in play must be using an interface?
As far as SpawnDev.BlazorJS.WebWorkers
is concerned, you only need to register services with an interface if you wish to use the GetService<TService>()
method to obtain an Interface proxy. See AsynCallDispatcher for ways to interact with services without registering interfaces.
It might be a caching issue. Worth trying.
In Chrome:
The exception states that the WebWorker's ServiceProvider does not contain a service registered as IUnitOfWork
(and previously UnitOfWork
). If your WebWorkers are loading the older, previous version of your app, before you added the IUnitOfWork or UnitOfWork service, that would explain the exact error message you are seeing.
I've followed your steps in Edge. Also, while dev tools is open, I often right click the refresh button to do "Empty cache and hard refresh" to clear out stuff which can cause this.
Unfortunately, neither helped.
To make sure my service wasn't causing issues on its own i tried the following (injecting the service and running it without a web worker), which worked fine:
public async Task<List<object>> GetToListAsync(Type entityType, CancellationToken cancellationToken = default, bool doSync = true)
{
if (doSync)
{
await _syncService.SyncRemoteWithLocal(entityType, cancellationToken);
}
return await _unitOfWork.GetListAsync(entityType,cancellationToken);
}
Generally speaking, I think I'm injecting and using the WebWorkerService as intended:
public class DynamicRepository
{
private readonly IUnitOfWork _unitOfWork;
private readonly ISyncListenerService _listenerService;
private readonly WebWorkerService _webWorkerService;
private readonly ISyncService _syncService;
public DynamicRepository(IUnitOfWork unitOfWork, ISyncListenerService listenerService, WebWorkerService webWorkerService, ISyncService syncService)
{
_unitOfWork = unitOfWork;
_listenerService = listenerService;
_listenerService.RegisterListener(this);
_webWorkerService = webWorkerService;
_syncService = syncService;
}
public async Task<List<object>> GetToListAsync(Type entityType, CancellationToken cancellationToken = default, bool doSync = true)
{
if (doSync)
{
Console.WriteLine("Getting ISyncService from _webWorkerService.TaskPool.GetService<ISyncService>()");
var webWorker = await _webWorkerService.GetWebWorker();
Console.WriteLine($"Got WebWorker instance Id {webWorker?.LocalInfo.InstanceId}");
var service = webWorker.GetService<ISyncService>();
Console.WriteLine($"Got ISyncService from _webWorkerService.TaskPool.GetService<ISyncService>() instance Id {_webWorkerService.InstanceId}");
await service.SyncRemoteWithLocal(entityType, cancellationToken);
Console.WriteLine("Finished SyncRemoteWithLocal using _webWorkerService.TaskPool.GetService<ISyncService>()");
}
return await _unitOfWork.GetListAsync(entityType,cancellationToken);
}
}
I do get the following lines before the error:
Got WebWorker instance Id 67411a44-4c1f-455a-9f57-2892bfc9d6af
Got ISyncService from _webWorkerService.TaskPool.GetService<ISyncService>() instance Id 67411a44-4c1f-455a-9f57-2892bfc9d6af
It is because it is registered as a scoped service. I am not sure exactly why that is causing the issue or if it is resolvable, but looking into it now.
One thing you can do is switch the registration scope. Scoped and Singleton are effectively the same thing in Blazor WebAssembly
I'm trying to figure out how to do it with Besql, which needs to be a singleton to work with UnitOfWork:
builder.Services.AddBesqlDbContextFactory<AppDbContext>(options =>
{
options.UseSqlite("Data Source=app.db");
options.UseSnakeCaseNamingConvention();
options.EnableDetailedErrors();
options.EnableSensitiveDataLogging(); // This enables logging parameter values
options.LogTo(Console.WriteLine, LogLevel.Error); // Add logging to console
});
Let me look into it and I will get back to you. Hopefully you won't have to change anything. Brb
Thanks for the speedy responses. I'll play around with Besql on my end to see if it can give me more control over registering it.
Changing all the services from scoped to singleton (which like you said, are the same thing in wasm anyways) did solve the problem!
Code for reference:
//add besql as singleton
builder.Services.AddSingleton<IBesqlStorage, BrowserCacheBesqlStorage>();
builder.Services.AddDbContextFactory<AppDbContext, BesqlDbContextFactory<AppDbContext>>(options =>
{
options.UseSqlite("Data Source=app.db");
options.UseSnakeCaseNamingConvention();
options.EnableDetailedErrors();
options.EnableSensitiveDataLogging(); // This enables logging parameter values
options.LogTo(Console.WriteLine, LogLevel.Error); // Add logging to console
}, ServiceLifetime.Singleton);
No I'm having a new interesting challenge where i sync the data in a web worker.. then read the data from the main thread, no results are returned ๐
when looking at this options.UseSqlite("Data Source=app.db");
, does the web worker have its own copy?
Edit: refreshing the page BEFORE using web workers had the same data loss effect, maybe it's a persistence issue from Besql or the way I implemented Besql.
My first guess is that this options.UseSqlite("Data Source=app.db");
is telling SQLite to store the database in the MEMFS file system that Blazor WASM uses, which is created at instance startup (part of EMScripten). It is the same place System.IO.File and System.IO.Directory read and write to in Blazor WASM. You could check by doing a File.Exists("app.db")
after writing your database.
I tried looking at the site I found that think is related: bitplatform.dev , but I didn't see much documentation.
Maybe because of the service worker clearing the cache(s) every time?
service-worker.js
// In development, always fetch from the network and do not enable offline support.
// This is because caching would make development more difficult (changes would not
// be reflected on the first load after each change).
self.addEventListener('fetch', () => { });
That line is there because it is the minimum required listener for a service worker. It is a service worker that does nothing. It does not do anything with the cache.
I'll create an issue with bitplatform to investigate. Many thanks for your help and patience!
Thank you for sponsoring me. ๐
I just uploaded version 2.2.100 which supports calling Singleton, Scoped, and Transient services in WebWorkers. Keyed services are not currently supported. Thank you for reporting issues. It really helps. ๐๐
Describe the bug Getting the following error when calling a service method using a web worker.
To Reproduce Steps to reproduce the behaviour:
Expected behavior The method to run
Screenshots N/A
Desktop (please complete the following information):
Additional context DynamicRepository.cs
SyncService.cs
UnitOfWork.cs
Program.cs