├── .gitignore ├── LICENSE ├── README.md ├── icon.svg ├── src ├── Assets │ └── mono-config.json ├── BlazorWorker.Demo.IoCExample │ ├── BlazorWorker.Demo.IoCExample.csproj │ ├── IMyServiceDependency.cs │ ├── MyIndexDBService.cs │ ├── MyIndexDBServiceStartup.cs │ ├── MyIocService.cs │ ├── MyServiceDependency.cs │ ├── MyServiceStartup.cs │ ├── ServiceCollectionHelper.cs │ └── SetupIndexedDBExtensions.cs ├── BlazorWorker.Demo │ ├── Client │ │ ├── App.razor │ │ ├── BlazorWorker.Demo.Client.csproj │ │ ├── Pages │ │ │ ├── BackgroundServiceMulti.razor │ │ │ ├── CoreExample.razor │ │ │ ├── Http.razor │ │ │ ├── Index.razor │ │ │ ├── IndexedDB.razor │ │ │ └── IoCExample.razor │ │ ├── Program.cs │ │ ├── _Imports.razor │ │ └── wwwroot │ │ │ ├── .nojekyll │ │ │ ├── 404.html │ │ │ ├── css │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ └── open-iconic │ │ │ │ ├── FONT-LICENSE │ │ │ │ ├── ICON-LICENSE │ │ │ │ ├── README.md │ │ │ │ └── font │ │ │ │ ├── css │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ └── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.svg │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── loading-icon.svg │ ├── ClientWasm32 │ │ ├── App.razor │ │ ├── BlazorWorker.Demo.Client.csproj │ │ ├── Pages │ │ │ ├── BackgroundServiceMulti.razor │ │ │ ├── CoreExample.razor │ │ │ ├── Http.razor │ │ │ ├── Index.razor │ │ │ ├── IndexedDB.razor │ │ │ └── IoCExample.razor │ │ ├── Program.cs │ │ ├── _Imports.razor │ │ └── wwwroot │ │ │ ├── .nojekyll │ │ │ ├── 404.html │ │ │ ├── css │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ └── open-iconic │ │ │ │ ├── FONT-LICENSE │ │ │ │ ├── ICON-LICENSE │ │ │ │ ├── README.md │ │ │ │ └── font │ │ │ │ ├── css │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ └── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.svg │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ └── loading-icon.svg │ ├── Net7 │ │ └── Client │ │ │ └── BlazorWorker.Demo.Client │ │ │ ├── App.razor │ │ │ ├── BlazorWorker.Demo.Client.csproj │ │ │ ├── Pages │ │ │ ├── BackgroundServiceMulti.razor │ │ │ ├── ComplexSerialization.razor │ │ │ ├── CoreExample.razor │ │ │ ├── Http.razor │ │ │ ├── Index.razor │ │ │ ├── IndexedDb.razor │ │ │ ├── IoCExample.razor │ │ │ ├── JsDirect.razor │ │ │ └── JsInteractions.razor │ │ │ ├── Program.cs │ │ │ ├── Shared │ │ │ ├── MainLayout.razor │ │ │ ├── MainLayout.razor.css │ │ │ ├── NavMenu.razor │ │ │ ├── NavMenu.razor.css │ │ │ └── SurveyPrompt.razor │ │ │ ├── _Imports.razor │ │ │ └── wwwroot │ │ │ ├── css │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ └── open-iconic │ │ │ │ ├── FONT-LICENSE │ │ │ │ ├── ICON-LICENSE │ │ │ │ ├── README.md │ │ │ │ └── font │ │ │ │ ├── css │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ └── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.svg │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ │ ├── dotnet.js.symbols │ │ │ ├── dotnet.timezones.blat │ │ │ ├── favicon.ico │ │ │ ├── favicon.png │ │ │ ├── icon-192.png │ │ │ ├── icudt.dat │ │ │ ├── index.html │ │ │ ├── loading-icon.svg │ │ │ ├── mono-config.json │ │ │ ├── sample-data │ │ │ └── weather.json │ │ │ └── supportFiles │ │ │ ├── 0_runtimeconfig.bin │ │ │ └── 1_dotnet.js.symbols │ ├── Net8 │ │ └── Client │ │ │ └── BlazorWorker.Demo.Client │ │ │ ├── App.razor │ │ │ ├── BlazorWorker.Demo.Client.csproj │ │ │ ├── Pages │ │ │ ├── BackgroundServiceMulti.razor │ │ │ ├── ComplexSerialization.razor │ │ │ ├── CoreExample.razor │ │ │ ├── Http.razor │ │ │ ├── Index.razor │ │ │ ├── IndexedDb.razor │ │ │ ├── IoCExample.razor │ │ │ ├── JsDirect.razor │ │ │ └── JsInteractions.razor │ │ │ ├── Program.cs │ │ │ ├── Shared │ │ │ ├── MainLayout.razor │ │ │ ├── MainLayout.razor.css │ │ │ ├── NavMenu.razor │ │ │ ├── NavMenu.razor.css │ │ │ └── SurveyPrompt.razor │ │ │ ├── _Imports.razor │ │ │ └── wwwroot │ │ │ ├── css │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ └── open-iconic │ │ │ │ ├── FONT-LICENSE │ │ │ │ ├── ICON-LICENSE │ │ │ │ ├── README.md │ │ │ │ └── font │ │ │ │ ├── css │ │ │ │ └── open-iconic-bootstrap.min.css │ │ │ │ └── fonts │ │ │ │ ├── open-iconic.eot │ │ │ │ ├── open-iconic.otf │ │ │ │ ├── open-iconic.svg │ │ │ │ ├── open-iconic.ttf │ │ │ │ └── open-iconic.woff │ │ │ ├── dotnet.js.symbols │ │ │ ├── dotnet.timezones.blat │ │ │ ├── favicon.ico │ │ │ ├── favicon.png │ │ │ ├── icon-192.png │ │ │ ├── icudt.dat │ │ │ ├── index.html │ │ │ ├── loading-icon.svg │ │ │ ├── mono-config.json │ │ │ ├── sample-data │ │ │ └── weather.json │ │ │ └── supportFiles │ │ │ ├── 0_runtimeconfig.bin │ │ │ └── 1_dotnet.js.symbols │ ├── Server │ │ ├── BlazorWorker.Demo.Server.csproj │ │ ├── Pages │ │ │ ├── Error.cshtml │ │ │ ├── Error.cshtml.cs │ │ │ └── Shared │ │ │ │ └── _Layout.cshtml │ │ ├── Program.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ ├── ServerWasm32 │ │ ├── BlazorWorker.Demo.Server.csproj │ │ ├── Pages │ │ │ ├── Error.cshtml │ │ │ ├── Error.cshtml.cs │ │ │ └── Shared │ │ │ │ └── _Layout.cshtml │ │ ├── Program.cs │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ ├── Shared │ │ ├── BlazorWorker.Demo.Shared.csproj │ │ ├── CoreMathsService.cs │ │ ├── JsDirectExample.cs │ │ ├── JsInteractionsExample.cs │ │ ├── MathsService.cs │ │ ├── SharedDemoExtensions.cs │ │ └── WebCallerService.cs │ └── SharedPages │ │ ├── BlazorWorker.Demo.SharedPages.csproj │ │ ├── Pages │ │ ├── BackgroundServiceMulti.razor │ │ ├── ComplexSerialization.razor │ │ ├── CoreExample.razor │ │ ├── Http.razor │ │ ├── Index.razor │ │ ├── IndexedDb.razor │ │ ├── IoCExamplePage.razor │ │ ├── JsDirect.razor │ │ └── JsInteractions.razor │ │ ├── Shared │ │ ├── CustomExpressionSerializer.cs │ │ ├── GithubSource.razor │ │ ├── MainLayout.razor │ │ ├── NavMenu.razor │ │ ├── NavMenuLink.razor │ │ └── NavMenuLinksModel.cs │ │ ├── _Imports.razor │ │ └── wwwroot │ │ ├── JsDirectExample.js │ │ └── JsInteractionsExample.js ├── BlazorWorker.Extensions.JSRuntime │ ├── BlazorWorker.Extensions.JSRuntime.csproj │ ├── BlazorWorker.Extensions.JSRuntime.xml │ ├── BlazorWorkerJSRuntime.cs │ ├── BlazorWorkerJsRuntimeSetupExtensions.cs │ ├── DefaultBlazorWorkerJSRuntimeSerializer.cs │ ├── DotNetObjectReferenceJsonConverter.cs │ ├── DotNetObjectReferenceJsonConverterFactory.cs │ ├── DotNetObjectReferenceTracker.cs │ ├── GenericNonPublicDelegateCache.cs │ ├── IBlazorWorkerJSRuntimeSerializer.cs │ ├── JSInvokeService.cs │ ├── icon.png │ └── wwwroot │ │ └── BlazorWorkerJSRuntime.js ├── BlazorWorker.ServiceFactory │ ├── BackgroundServiceWorkerException.cs │ ├── BlazorWorker.BackgroundServiceFactory.csproj │ ├── BlazorWorker.BackgroundServiceFactory.xml │ ├── WorkerBackgroundServiceDependencies.cs │ ├── WorkerBackgroundServiceExtensions.cs │ ├── WorkerBackgroundServiceProxy.cs │ ├── WorkerInitExtension.cs │ └── icon.png ├── BlazorWorker.WorkerBackgroundService │ ├── AssemblyInfo1.cs │ ├── BaseMessage.cs │ ├── BlazorWorker.WorkerBackgroundService.csproj │ ├── BlazorWorker.WorkerBackgroundService.xml │ ├── DefaultMessageSerializer.cs │ ├── DisposeInstance.cs │ ├── DisposeInstanceComplete.cs │ ├── EventHandle.cs │ ├── EventHandlerWrapper.cs │ ├── EventRaised.cs │ ├── IEventWrapper.cs │ ├── IExpressionSerializer.cs │ ├── ISerializer.cs │ ├── IWorkerBackgroundService.cs │ ├── IWorkerBackgroundServiceFactory.cs │ ├── InitInstance.cs │ ├── InitInstanceComplete.cs │ ├── InitInstanceFromFactory.cs │ ├── InitInstanceFromFactoryComplete.cs │ ├── InitWorkerComplete.cs │ ├── LinkerConfig.xml │ ├── MessageHandlerRegistry.cs │ ├── MethodCallParams.cs │ ├── MethodCallResult.cs │ ├── RegisterEvent.cs │ ├── SerializeLinqExpressionJsonSerializerBase.cs │ ├── SerializeLinqExpressionSerializer.cs │ ├── UnRegisterEvent.cs │ ├── WebWorkerOptions.cs │ └── WorkerInstanceManager.cs ├── BlazorWorker.WorkerCore │ ├── BlazorWorker.WorkerCore.csproj │ ├── BlazorWorker.WorkerCore.xml │ ├── DOMObject.cs │ ├── FetchResponse.cs │ ├── IWorker.cs │ ├── IWorkerMessageService.cs │ ├── InitOptions.cs │ ├── InstanceWrapper.cs │ ├── LinkerConfig.xml │ ├── MessageService.cs │ ├── Program.cs │ ├── SimpleInstanceService │ │ ├── CSVSerializer.cs │ │ ├── DisposeInstanceRequest.cs │ │ ├── DisposeResult.cs │ │ ├── ISimpleInstanceService.cs │ │ ├── InitInstanceRequest.cs │ │ ├── InitInstanceResult.cs │ │ ├── InitServiceResult.cs │ │ ├── InjectableMessageService.cs │ │ └── SimpleInstanceService.cs │ ├── TaskRegister.cs │ └── WebAssemblyBindingsProxy │ │ ├── JSObject.cs │ │ └── Runtime.cs ├── BlazorWorker.sln ├── BlazorWorker │ ├── BlazorWorker.Core.csproj │ ├── BlazorWorker.Core.xml │ ├── BlazorWorker.js │ ├── CoreInstanceService │ │ ├── CoreInstanceHandle.cs │ │ ├── CoreInstanceService.cs │ │ ├── ICoreInstanceService.cs │ │ ├── SimpleInstanceServiceExtension.cs │ │ ├── WorkerException.cs │ │ ├── WorkerInstanceDisposeException.cs │ │ └── WorkerInstanceInitializeException.cs │ ├── DependencyHintAttribute.cs │ ├── IWorkerFactory.cs │ ├── InstanceHandle.cs │ ├── MonoTypeHelper.cs │ ├── ScriptLoader.cs │ ├── SetupExtensions.cs │ ├── SimpleInstanceService │ │ └── SimpleInstanceServiceProxy.cs │ ├── WorkerFactory.cs │ ├── WorkerProxy.cs │ ├── WorkerProxyDependencies.cs │ └── icon.png └── nuget.config └── test └── BlazorWorker.Extensions.JSRuntimeTests ├── BlazorWorker.Extensions.JSRuntimeTests.csproj └── GenericNonPublicDelegateCacheTest.cs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Tor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/BlazorWorker.Demo.IoCExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0;net8.0 5 | Debug;Release;Nuget 6 | 7 | 8 | 9 | 1701;1702;1591;CA1416 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/IMyServiceDependency.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.Demo.IoCExample 2 | { 3 | public interface IMyServiceDependency 4 | { 5 | public int Five(); 6 | } 7 | } -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/MyIndexDBService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading.Tasks; 5 | using TG.Blazor.IndexedDB; 6 | 7 | namespace BlazorWorker.Demo.IoCExample 8 | { 9 | public class MyIndexDBService 10 | { 11 | private readonly IJSRuntime jsRuntime; 12 | private readonly IndexedDBManager indexedDBManager; 13 | private bool isIndexDBManagerInitialized; 14 | 15 | public MyIndexDBService( 16 | IJSRuntime jsRuntime, 17 | IndexedDBManager indexDBManager) 18 | { 19 | //Console.WriteLine($"{nameof(MyIndexDBService)} instance created"); 20 | this.jsRuntime = jsRuntime; 21 | this.indexedDBManager = indexDBManager; 22 | } 23 | 24 | public async Task GetPersonName(long id) 25 | { 26 | try 27 | { 28 | //Console.WriteLine($"{nameof(GetPersonName)}({id}) called."); 29 | await EnsureInitializedAsync(); 30 | //Console.WriteLine($"{nameof(GetPersonName)}({id}): Get Store name..."); 31 | var storeName = indexedDBManager.Stores[0].Name; 32 | var testPersons = (await this.indexedDBManager.GetRecords(storeName)); 33 | foreach (var item in testPersons) 34 | { 35 | if (item.Id == id) 36 | { 37 | return item.Name ?? "[empty name]"; 38 | } 39 | } 40 | 41 | return "N/A"; 42 | 43 | } 44 | catch (System.Exception e) 45 | { 46 | Console.Error.WriteLine($"{nameof(GetPersonName)} :{e}"); 47 | throw; 48 | } 49 | } 50 | 51 | private async Task EnsureInitializedAsync() 52 | { 53 | if (!isIndexDBManagerInitialized) 54 | { 55 | // The following is a workaround as indexedDb.Blazor.js explicitly references "window" 56 | await this.jsRuntime.InvokeVoidAsync("eval", "(() => { self.window = self; return null; })()"); 57 | await this.jsRuntime.InvokeVoidAsync("importLocalScripts", "_content/TG.Blazor.IndexedDB/indexedDb.Blazor.js"); 58 | 59 | isIndexDBManagerInitialized = true; 60 | } 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/MyIndexDBServiceStartup.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Extensions.JSRuntime; 2 | using BlazorWorker.WorkerCore; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | 6 | namespace BlazorWorker.Demo.IoCExample 7 | { 8 | public class MyIndexDBServiceStartup 9 | { 10 | private readonly IServiceProvider serviceProvider; 11 | private readonly IWorkerMessageService workerMessageService; 12 | 13 | /// 14 | /// The constructor uses the built-in injection for library-native services such as the . 15 | /// 16 | /// 17 | public MyIndexDBServiceStartup(IWorkerMessageService workerMessageService) 18 | { 19 | this.workerMessageService = workerMessageService; 20 | serviceProvider = ServiceCollectionHelper.BuildServiceProviderFromMethod(Configure); 21 | } 22 | 23 | public T Resolve()=> serviceProvider.GetService(); 24 | 25 | public void Configure(IServiceCollection services) 26 | { 27 | services.AddTransient() 28 | .AddBlazorWorkerJsRuntime() 29 | .AddSingleton() 30 | .AddSingleton(workerMessageService) 31 | .AddIndexedDbDemoPersonConfig(); 32 | } 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/MyIocService.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using Microsoft.JSInterop; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorWorker.Demo.IoCExample 7 | { 8 | public class Something { 9 | public string Value { get; set; } 10 | } 11 | public class MyIocService 12 | { 13 | 14 | private int FiveCalledCounter = 0; 15 | 16 | public MyIocService( 17 | IWorkerMessageService workerMessageService, 18 | IMyServiceDependency aServiceDependency, 19 | IJSRuntime jSRuntime) 20 | { 21 | WorkerMessageService = workerMessageService; 22 | AServiceDependency = aServiceDependency; 23 | JSRuntime = jSRuntime; 24 | } 25 | 26 | public async Task Five() 27 | { 28 | this.FiveCalled?.Invoke(this, FiveCalledCounter++); 29 | try 30 | { 31 | Console.WriteLine($"MyIocService: Attempting JsRuntime InvokeAsync..."); 32 | var theNumberOfTheBeast = await this.JSRuntime.InvokeAsync("eval", 33 | "(function(){ console.log('Hello world invoke call from MyIocService'); return 666; })()"); 34 | 35 | Console.WriteLine($"{theNumberOfTheBeast} : The number of the beast"); 36 | return this.AServiceDependency.Five(); 37 | } 38 | finally 39 | { 40 | if (this.FiveCalledCounter > 2) 41 | { 42 | await this.WorkerMessageService.PostMessageAsync($"{nameof(FiveCalledCounter)} has been called more than 2 times: {this.FiveCalledCounter} times!"); 43 | } 44 | } 45 | } 46 | 47 | public event EventHandler FiveCalled; 48 | 49 | public IWorkerMessageService WorkerMessageService { get; } 50 | 51 | public IMyServiceDependency AServiceDependency { get; } 52 | public IJSRuntime JSRuntime { get; } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/MyServiceDependency.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.Demo.IoCExample 2 | { 3 | public class MyServiceDependency : IMyServiceDependency 4 | { 5 | public int Five() 6 | { 7 | return 5; 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/MyServiceStartup.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.JSInterop; 4 | using System; 5 | 6 | using BlazorWorker.Extensions.JSRuntime; 7 | 8 | namespace BlazorWorker.Demo.IoCExample 9 | { 10 | public class MyServiceStartup 11 | { 12 | private readonly IServiceProvider serviceProvider; 13 | private readonly IWorkerMessageService workerMessageService; 14 | 15 | /// 16 | /// The constructor uses the built-in injection for library-native services such as the . 17 | /// 18 | /// 19 | public MyServiceStartup(IWorkerMessageService workerMessageService) 20 | { 21 | this.workerMessageService = workerMessageService; 22 | serviceProvider = ServiceCollectionHelper.BuildServiceProviderFromMethod(Configure); 23 | } 24 | 25 | public T Resolve() => serviceProvider.GetService(); 26 | 27 | public void Configure(IServiceCollection services) 28 | { 29 | services.AddTransient() 30 | .AddBlazorWorkerJsRuntime() 31 | .AddTransient() 32 | .AddSingleton(workerMessageService); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/ServiceCollectionHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.JSInterop; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace BlazorWorker.Demo.IoCExample 9 | { 10 | public static class ServiceCollectionHelper 11 | { 12 | public delegate void Configure(IServiceCollection services); 13 | 14 | public static IServiceProvider BuildServiceProviderFromMethod(Configure configureMethod) 15 | { 16 | var serviceCollection = new ServiceCollection(); 17 | configureMethod(serviceCollection); 18 | return serviceCollection.BuildServiceProvider(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo.IoCExample/SetupIndexedDBExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System.Collections.Generic; 3 | using TG.Blazor.IndexedDB; 4 | 5 | namespace BlazorWorker.Demo.IoCExample 6 | { 7 | public static class SetupIndexedDBExtensions 8 | { 9 | public static IServiceCollection AddIndexedDbDemoPersonConfig(this IServiceCollection services) 10 | { 11 | services.AddIndexedDB(dbStore => 12 | { 13 | dbStore.DbName = "TheFactory"; //example name 14 | dbStore.Version = 1; 15 | 16 | dbStore.Stores.Add(new StoreSchema 17 | { 18 | Name = "TestPersons", 19 | PrimaryKey = new IndexSpec { Name = "id", KeyPath = "id", Auto = true }, 20 | Indexes = new List 21 | { 22 | new IndexSpec{ Name = "name", KeyPath = "name", Auto = false }, 23 | } 24 | }); 25 | }); 26 | 27 | return services; 28 | } 29 | } 30 | 31 | public class Person 32 | { 33 | public long? Id { get; set; } 34 | public string Name { get; set; } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Sorry, there's nothing at this address.

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/BlazorWorker.Demo.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0 5 | Debug;Release;Nuget 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | $(DefineConstants);NET5 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/Pages/BackgroundServiceMulti.razor: -------------------------------------------------------------------------------- 1 | @page "/BackgroundServiceMulti" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/Pages/CoreExample.razor: -------------------------------------------------------------------------------- 1 | @page "/CoreExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/Pages/Http.razor: -------------------------------------------------------------------------------- 1 | @page "/Http" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/Pages/IndexedDB.razor: -------------------------------------------------------------------------------- 1 | @page "/IndexedDB" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/Pages/IoCExample.razor: -------------------------------------------------------------------------------- 1 | @page "/IoCExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Core; 2 | using BlazorWorker.Demo.IoCExample; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorWorker.Demo.Client 10 | { 11 | public class Program 12 | { 13 | public static async Task Main(string[] args) 14 | { 15 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 16 | builder.RootComponents.Add("app"); 17 | builder.Services.AddWorkerFactory(); 18 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 19 | builder.Services.AddIndexedDbDemoPersonConfig(); 20 | 21 | await builder.Build().RunAsync(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.JSInterop 7 | @using BlazorWorker.Demo.Client 8 | @using BlazorWorker.BackgroundServiceFactory 9 | @using BlazorWorker.WorkerBackgroundService 10 | @using BlazorWorker.Demo.Shared 11 | @using BlazorWorker.Core -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Client/wwwroot/.nojekyll -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/404.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | BlazorFileReader 6 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Sorry, there's nothing at this address.

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/BlazorWorker.Demo.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | Debug;Release;Nuget 6 | 3.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | $(DefineConstants);NETSTANDARD21 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/Pages/BackgroundServiceMulti.razor: -------------------------------------------------------------------------------- 1 | @page "/BackgroundServiceMulti" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/Pages/CoreExample.razor: -------------------------------------------------------------------------------- 1 | @page "/CoreExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/Pages/Http.razor: -------------------------------------------------------------------------------- 1 | @page "/Http" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/Pages/IndexedDB.razor: -------------------------------------------------------------------------------- 1 | @page "/IndexedDB" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/Pages/IoCExample.razor: -------------------------------------------------------------------------------- 1 | @page "/IoCExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Core; 2 | using BlazorWorker.Demo.IoCExample; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorWorker.Demo.Client 10 | { 11 | public class Program 12 | { 13 | public static async Task Main(string[] args) 14 | { 15 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 16 | builder.RootComponents.Add("app"); 17 | builder.Services.AddWorkerFactory(); 18 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 19 | builder.Services.AddIndexedDbDemoPersonConfig(); 20 | 21 | await builder.Build().RunAsync(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.JSInterop 7 | @using BlazorWorker.Demo.Client 8 | @using BlazorWorker.BackgroundServiceFactory 9 | @using BlazorWorker.WorkerBackgroundService 10 | @using BlazorWorker.Demo.Shared 11 | @using BlazorWorker.Core -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/ClientWasm32/wwwroot/.nojekyll -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/404.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | BlazorFileReader 6 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/ClientWasm32/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ClientWasm32/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/ClientWasm32/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/BlazorWorker.Demo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/BackgroundServiceMulti.razor: -------------------------------------------------------------------------------- 1 | @page "/BackgroundServiceMulti" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/ComplexSerialization.razor: -------------------------------------------------------------------------------- 1 | @page "/ComplexSerialization" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/CoreExample.razor: -------------------------------------------------------------------------------- 1 | @page "/CoreExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/Http.razor: -------------------------------------------------------------------------------- 1 | @page "/Http" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/IndexedDb.razor: -------------------------------------------------------------------------------- 1 | @page "/IndexedDb" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/IoCExample.razor: -------------------------------------------------------------------------------- 1 | @page "/IoCExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/JsDirect.razor: -------------------------------------------------------------------------------- 1 | @page "/JsDirect" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Pages/JsInteractions.razor: -------------------------------------------------------------------------------- 1 | @page "/JsInteractions" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Demo.Client; 2 | using BlazorWorker.Demo.Shared; 3 | using Microsoft.AspNetCore.Components.Web; 4 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 5 | 6 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 7 | builder.RootComponents.Add("#app"); 8 | builder.RootComponents.Add("head::after"); 9 | 10 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 11 | builder.Services.AddDemoDependencies(); 12 | 13 | await builder.Build().RunAsync(); 14 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | @Body 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row:not(.auth) { 41 | display: none; 42 | } 43 | 44 | .top-row.auth { 45 | justify-content: space-between; 46 | } 47 | 48 | .top-row ::deep a, .top-row ::deep .btn-link { 49 | margin-left: 0; 50 | } 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .page { 55 | flex-direction: row; 56 | } 57 | 58 | .sidebar { 59 | width: 250px; 60 | height: 100vh; 61 | position: sticky; 62 | top: 0; 63 | } 64 | 65 | .top-row { 66 | position: sticky; 67 | top: 0; 68 | z-index: 1; 69 | } 70 | 71 | .top-row.auth ::deep a:first-child { 72 | flex: 1; 73 | text-align: right; 74 | width: 0; 75 | } 76 | 77 | .top-row, article { 78 | padding-left: 2rem !important; 79 | padding-right: 1.5rem !important; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 18 |
19 | 20 | @code { 21 | private bool collapseNavMenu = true; 22 | 23 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 24 | 25 | private void ToggleNavMenu() 26 | { 27 | collapseNavMenu = !collapseNavMenu; 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/Shared/SurveyPrompt.razor: -------------------------------------------------------------------------------- 1 | 
2 | 3 | @Title 4 | 5 | 6 | Please take our 7 | brief survey 8 | 9 | and tell us what you think. 10 |
11 | 12 | @code { 13 | // Demonstrates how a parent component can supply parameters 14 | [Parameter] 15 | public string? Title { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using BlazorWorker.Demo.Client 10 | @using BlazorWorker.Demo.Client.Shared 11 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/dotnet.timezones.blat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/dotnet.timezones.blat -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/favicon.png -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/icudt.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/icudt.dat -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BlazorWorker.Demo.Client 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 | An unhandled error has occurred. 29 | Reload 30 | 🗙 31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2022-01-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2022-01-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2022-01-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2022-01-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2022-01-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net7/Client/BlazorWorker.Demo.Client/wwwroot/supportFiles/0_runtimeconfig.bin: -------------------------------------------------------------------------------- 1 | 2 | MMicrosoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmabilitytruefSystem.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerializationfalse9System.Resources.ResourceManager.AllowCustomResourceTypesfalse 2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 | 13 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/BlazorWorker.Demo.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/BackgroundServiceMulti.razor: -------------------------------------------------------------------------------- 1 | @page "/BackgroundServiceMulti" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/ComplexSerialization.razor: -------------------------------------------------------------------------------- 1 | @page "/ComplexSerialization" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/CoreExample.razor: -------------------------------------------------------------------------------- 1 | @page "/CoreExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/Http.razor: -------------------------------------------------------------------------------- 1 | @page "/Http" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/IndexedDb.razor: -------------------------------------------------------------------------------- 1 | @page "/IndexedDb" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/IoCExample.razor: -------------------------------------------------------------------------------- 1 | @page "/IoCExample" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/JsDirect.razor: -------------------------------------------------------------------------------- 1 | @page "/JsDirect" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/JsInteractions.razor: -------------------------------------------------------------------------------- 1 | @page "/JsInteractions" 2 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Demo.Client; 2 | using BlazorWorker.Demo.Shared; 3 | using Microsoft.AspNetCore.Components.Web; 4 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 5 | 6 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 7 | builder.RootComponents.Add("#app"); 8 | builder.RootComponents.Add("head::after"); 9 | 10 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 11 | builder.Services.AddDemoDependencies(); 12 | 13 | await builder.Build().RunAsync(); 14 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | @Body 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Shared/MainLayout.razor.css: -------------------------------------------------------------------------------- 1 | .page { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | main { 8 | flex: 1; 9 | } 10 | 11 | .sidebar { 12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); 13 | } 14 | 15 | .top-row { 16 | background-color: #f7f7f7; 17 | border-bottom: 1px solid #d6d5d5; 18 | justify-content: flex-end; 19 | height: 3.5rem; 20 | display: flex; 21 | align-items: center; 22 | } 23 | 24 | .top-row ::deep a, .top-row ::deep .btn-link { 25 | white-space: nowrap; 26 | margin-left: 1.5rem; 27 | text-decoration: none; 28 | } 29 | 30 | .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { 31 | text-decoration: underline; 32 | } 33 | 34 | .top-row ::deep a:first-child { 35 | overflow: hidden; 36 | text-overflow: ellipsis; 37 | } 38 | 39 | @media (max-width: 640.98px) { 40 | .top-row:not(.auth) { 41 | display: none; 42 | } 43 | 44 | .top-row.auth { 45 | justify-content: space-between; 46 | } 47 | 48 | .top-row ::deep a, .top-row ::deep .btn-link { 49 | margin-left: 0; 50 | } 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .page { 55 | flex-direction: row; 56 | } 57 | 58 | .sidebar { 59 | width: 250px; 60 | height: 100vh; 61 | position: sticky; 62 | top: 0; 63 | } 64 | 65 | .top-row { 66 | position: sticky; 67 | top: 0; 68 | z-index: 1; 69 | } 70 | 71 | .top-row.auth ::deep a:first-child { 72 | flex: 1; 73 | text-align: right; 74 | width: 0; 75 | } 76 | 77 | .top-row, article { 78 | padding-left: 2rem !important; 79 | padding-right: 1.5rem !important; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  9 | 10 |
11 | 18 |
19 | 20 | @code { 21 | private bool collapseNavMenu = true; 22 | 23 | private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; 24 | 25 | private void ToggleNavMenu() 26 | { 27 | collapseNavMenu = !collapseNavMenu; 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Shared/NavMenu.razor.css: -------------------------------------------------------------------------------- 1 | .navbar-toggler { 2 | background-color: rgba(255, 255, 255, 0.1); 3 | } 4 | 5 | .top-row { 6 | height: 3.5rem; 7 | background-color: rgba(0,0,0,0.4); 8 | } 9 | 10 | .navbar-brand { 11 | font-size: 1.1rem; 12 | } 13 | 14 | .oi { 15 | width: 2rem; 16 | font-size: 1.1rem; 17 | vertical-align: text-top; 18 | top: -2px; 19 | } 20 | 21 | .nav-item { 22 | font-size: 0.9rem; 23 | padding-bottom: 0.5rem; 24 | } 25 | 26 | .nav-item:first-of-type { 27 | padding-top: 1rem; 28 | } 29 | 30 | .nav-item:last-of-type { 31 | padding-bottom: 1rem; 32 | } 33 | 34 | .nav-item ::deep a { 35 | color: #d7d7d7; 36 | border-radius: 4px; 37 | height: 3rem; 38 | display: flex; 39 | align-items: center; 40 | line-height: 3rem; 41 | } 42 | 43 | .nav-item ::deep a.active { 44 | background-color: rgba(255,255,255,0.25); 45 | color: white; 46 | } 47 | 48 | .nav-item ::deep a:hover { 49 | background-color: rgba(255,255,255,0.1); 50 | color: white; 51 | } 52 | 53 | @media (min-width: 641px) { 54 | .navbar-toggler { 55 | display: none; 56 | } 57 | 58 | .collapse { 59 | /* Never collapse the sidebar for wide screens */ 60 | display: block; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Shared/SurveyPrompt.razor: -------------------------------------------------------------------------------- 1 | 
2 | 3 | @Title 4 | 5 | 6 | Please take our 7 | brief survey 8 | 9 | and tell us what you think. 10 |
11 | 12 | @code { 13 | // Demonstrates how a parent component can supply parameters 14 | [Parameter] 15 | public string? Title { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using BlazorWorker.Demo.Client 10 | @using BlazorWorker.Demo.Client.Shared 11 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/dotnet.timezones.blat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/dotnet.timezones.blat -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/favicon.png -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/icon-192.png -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/icudt.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/icudt.dat -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BlazorWorker.Demo.Client 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 | An unhandled error has occurred. 29 | Reload 30 | 🗙 31 |
32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2022-01-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2022-01-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2022-01-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2022-01-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2022-01-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/wwwroot/supportFiles/0_runtimeconfig.bin: -------------------------------------------------------------------------------- 1 | 2 | MMicrosoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmabilitytruefSystem.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerializationfalse9System.Resources.ResourceManager.AllowCustomResourceTypesfalse 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | $(DefineConstants);NETCOREAPP31 21 | 22 | 23 | 24 | $(DefineConstants);NET5 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Server/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model BlazorWorker.Demo.Server.Pages.ErrorModel 3 | @{ 4 | Layout = "_Layout"; 5 | ViewData["Title"] = "Error"; 6 | } 7 | 8 |

Error.

9 |

An error occurred while processing your request.

10 | 11 | @if (Model.ShowRequestId) 12 | { 13 |

14 | Request ID: @Model.RequestId 15 |

16 | } 17 | 18 |

Development Mode

19 |

20 | Swapping to the Development environment displays detailed information about the error that occurred. 21 |

22 |

23 | The Development environment shouldn't be enabled for deployed applications. 24 | It can result in displaying sensitive information from exceptions to end users. 25 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 26 | and restarting the app. 27 |

28 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace BlazorWorker.Demo.Server.Pages 11 | { 12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 13 | public class ErrorModel : PageModel 14 | { 15 | public string RequestId { get; set; } 16 | 17 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 18 | 19 | private readonly ILogger _logger; 20 | 21 | public ErrorModel(ILogger logger) 22 | { 23 | _logger = logger; 24 | } 25 | 26 | public void OnGet() 27 | { 28 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Server/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | @ViewBag.Title 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | @RenderBody() 16 |
17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace BlazorWorker.Demo.Server 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Server/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace BlazorWorker.Demo.Server 8 | { 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | 23 | services.AddControllersWithViews(); 24 | services.AddRazorPages(); 25 | } 26 | 27 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 28 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 29 | { 30 | if (env.IsDevelopment()) 31 | { 32 | app.UseDeveloperExceptionPage(); 33 | app.UseWebAssemblyDebugging(); 34 | } 35 | else 36 | { 37 | app.UseExceptionHandler("/Error"); 38 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 39 | app.UseHsts(); 40 | } 41 | 42 | app.UseHttpsRedirection(); 43 | app.UseBlazorFrameworkFiles(); 44 | app.UseStaticFiles(); 45 | 46 | app.UseRouting(); 47 | 48 | app.UseEndpoints(endpoints => 49 | { 50 | endpoints.MapRazorPages(); 51 | endpoints.MapControllers(); 52 | endpoints.MapFallbackToFile("index.html"); 53 | }); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/BlazorWorker.Demo.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | $(DefineConstants);NETCOREAPP31 22 | 23 | 24 | 25 | $(DefineConstants);NET5 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model BlazorWorker.Demo.Server.Pages.ErrorModel 3 | @{ 4 | Layout = "_Layout"; 5 | ViewData["Title"] = "Error"; 6 | } 7 | 8 |

Error.

9 |

An error occurred while processing your request.

10 | 11 | @if (Model.ShowRequestId) 12 | { 13 |

14 | Request ID: @Model.RequestId 15 |

16 | } 17 | 18 |

Development Mode

19 |

20 | Swapping to the Development environment displays detailed information about the error that occurred. 21 |

22 |

23 | The Development environment shouldn't be enabled for deployed applications. 24 | It can result in displaying sensitive information from exceptions to end users. 25 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 26 | and restarting the app. 27 |

28 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.AspNetCore.Mvc.RazorPages; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace BlazorWorker.Demo.Server.Pages 11 | { 12 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 13 | public class ErrorModel : PageModel 14 | { 15 | public string RequestId { get; set; } 16 | 17 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 18 | 19 | private readonly ILogger _logger; 20 | 21 | public ErrorModel(ILogger logger) 22 | { 23 | _logger = logger; 24 | } 25 | 26 | public void OnGet() 27 | { 28 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | @ViewBag.Title 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | @RenderBody() 16 |
17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace BlazorWorker.Demo.Server 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | 7 | namespace BlazorWorker.Demo.Server 8 | { 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | 23 | services.AddControllersWithViews(); 24 | } 25 | 26 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 27 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 28 | { 29 | if (env.IsDevelopment()) 30 | { 31 | app.UseDeveloperExceptionPage(); 32 | app.UseWebAssemblyDebugging(); 33 | } 34 | else 35 | { 36 | app.UseExceptionHandler("/Error"); 37 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 38 | app.UseHsts(); 39 | } 40 | 41 | app.UseHttpsRedirection(); 42 | app.UseBlazorFrameworkFiles(); 43 | app.UseStaticFiles(); 44 | 45 | app.UseRouting(); 46 | 47 | app.UseEndpoints(endpoints => 48 | { 49 | endpoints.MapControllers(); 50 | endpoints.MapFallbackToFile("index.html"); 51 | }); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/ServerWasm32/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Shared/BlazorWorker.Demo.Shared.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0;net8.0 5 | Debug;Release;Nuget 6 | true 7 | 8 | 9 | 10 | 1701;1702;1591;CA1416 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Shared/CoreMathsService.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace BlazorWorker.Demo.Shared 5 | { 6 | /// 7 | /// This service runs in the worker. 8 | /// Uses hand-written seriaization of messages. 9 | /// 10 | public class CoreMathsService 11 | { 12 | public static readonly string EventsPi = $"Events.{nameof(MathsService.Pi)}"; 13 | public static readonly string ResultMessage = $"Methods.{nameof(MathsService.EstimatePI)}.Result"; 14 | 15 | private readonly MathsService mathsService; 16 | private readonly IWorkerMessageService messageService; 17 | 18 | public CoreMathsService(IWorkerMessageService messageService) 19 | { 20 | this.messageService = messageService; 21 | this.messageService.IncomingMessage += OnMessage; 22 | mathsService = new MathsService(); 23 | mathsService.Pi += (s, progress) => messageService.PostMessageAsync($"{EventsPi}:{progress.Progress}"); 24 | } 25 | 26 | private void OnMessage(object sender, string message) 27 | { 28 | if (message.StartsWith(nameof(mathsService.EstimatePI))) 29 | { 30 | var messageParams = message.Substring(nameof(mathsService.EstimatePI).Length).Trim(); 31 | var rx = new Regex(@"\((?[^\)]+)\)"); 32 | var arg0 = rx.Match(messageParams).Groups["arg"].Value.Trim(); 33 | var iterations = int.Parse(arg0); 34 | mathsService.EstimatePI(iterations).ContinueWith(t => 35 | messageService.PostMessageAsync($"{ResultMessage}:{t.Result}")); 36 | return; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Shared/JsDirectExample.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.BackgroundServiceFactory; 2 | using BlazorWorker.Core; 3 | using BlazorWorker.WorkerBackgroundService; 4 | using BlazorWorker.WorkerCore; 5 | using Microsoft.JSInterop; 6 | using System; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorWorker.Demo.Shared 10 | { 11 | public partial class JsDirectExample 12 | { 13 | private readonly IWorkerFactory workerFactory; 14 | private readonly IJSRuntime jsRuntime; 15 | private IWorkerBackgroundService service; 16 | private long workerId; 17 | public event EventHandler LogHandler; 18 | 19 | public JsDirectExample(IWorkerFactory workerFactory, IJSRuntime jsRuntime) 20 | { 21 | this.workerFactory = workerFactory; 22 | this.jsRuntime = jsRuntime; 23 | } 24 | 25 | private void Log(string message) 26 | { 27 | LogHandler?.Invoke(this, message); 28 | } 29 | 30 | public async Task Execute() 31 | { 32 | if (this.service == null) 33 | { 34 | Log("Execute: Creating worker..."); 35 | var worker = await this.workerFactory.CreateAsync(); 36 | this.workerId = worker.Identifier; 37 | Log("Execute: Creating service..."); 38 | this.service = await worker.CreateBackgroundServiceAsync(); 39 | 40 | Log("Execute: Setting up main js..."); 41 | 42 | // Method setupJsDirectForWorker is defined in BlazorWorker.Demo.SharedPages/wwwroot/JsDirectExample.js 43 | await this.jsRuntime.InvokeVoidAsync("setupJsDirectForWorker", this.workerId); 44 | } 45 | 46 | Log("Execute: Calling ExecuteJsDirect on worker..."); 47 | await service.RunAsync(s => s.ExecuteJsDirect()); 48 | Log("Execute: Done"); 49 | } 50 | 51 | } 52 | 53 | public class JsDirectExampleWorkerService 54 | { 55 | private readonly IWorkerMessageService messageService; 56 | public JsDirectExampleWorkerService(IWorkerMessageService messageService) 57 | { 58 | this.messageService = messageService; 59 | } 60 | public async Task ExecuteJsDirect() 61 | { 62 | await messageService.PostMessageJsDirectAsync("Hello main js thread."); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Shared/MathsService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Numerics; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorWorker.Demo.Shared 10 | { 11 | public class PiProgress 12 | { 13 | public int Progress { get; set; } 14 | } 15 | 16 | 17 | /// 18 | /// This service runs insinde the worker. 19 | /// 20 | public class MathsService 21 | { 22 | public event EventHandler Pi; 23 | 24 | private IEnumerable AlternatingSequence(int start = 0) 25 | { 26 | int i; 27 | bool flip; 28 | if (start == 0) 29 | { 30 | yield return 1; 31 | i = 1; 32 | flip = false; 33 | } 34 | else 35 | { 36 | i = (start * 2) - 1; 37 | flip = start % 2 == 0; 38 | } 39 | 40 | while (true) yield return ((flip = !flip) ? -1 : 1) * (i += 2); 41 | } 42 | 43 | public async Task EstimatePI(int sumLength) 44 | { 45 | var lastReport = 0; 46 | await Task.Delay(100); 47 | return (4 * AlternatingSequence().Take(sumLength) 48 | .Select((x, i) => { 49 | // Keep reporting events down a bit, serialization is expensive! 50 | var progressDelta = (Math.Abs(i - lastReport) / (double)sumLength) * 100; 51 | if (progressDelta > 3 || i >= sumLength - 1) 52 | { 53 | lastReport = i; 54 | Pi?.Invoke(this, new PiProgress() { Progress = i }); 55 | } 56 | return x; }) 57 | .Sum(x => 1.0 / x)); 58 | } 59 | 60 | public double EstimatePISlice(int sumStart, int sumLength) 61 | { 62 | Console.WriteLine($"EstimatePISlice({sumStart},{sumLength})"); 63 | var lastReport = 0; 64 | return AlternatingSequence(sumStart) 65 | .Take(sumLength) 66 | .Select((x, i) => { 67 | 68 | // Keep reporting events down a bit, serialization is expensive! 69 | var progressDelta = (Math.Abs(i - lastReport) / (double)sumLength) * 100; 70 | if (progressDelta > 3 || i >= sumLength - 1) 71 | { 72 | lastReport = i; 73 | Pi?.Invoke(this, new PiProgress() { Progress = i }); 74 | } 75 | return x; 76 | }) 77 | .Sum(x => 1.0 / x); 78 | 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Shared/SharedDemoExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Core; 2 | using BlazorWorker.Demo.IoCExample; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace BlazorWorker.Demo.Shared 6 | { 7 | public static class SharedDemoExtensions 8 | { 9 | public static IServiceCollection AddDemoDependencies(this IServiceCollection serviceCollection) 10 | { 11 | serviceCollection.AddWorkerFactory(); 12 | serviceCollection.AddIndexedDbDemoPersonConfig(); 13 | serviceCollection.AddTransient(); 14 | serviceCollection.AddTransient(); 15 | return serviceCollection; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/Shared/WebCallerService.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.Demo.Shared 5 | { 6 | public class WebCallerService 7 | { 8 | public HttpClient HttpClient { get; } 9 | 10 | public WebCallerService(HttpClient httpClient) 11 | { 12 | HttpClient = httpClient; 13 | } 14 | 15 | public async Task CallGitHubApi() 16 | { 17 | var github = await HttpClient.GetAsync("https://api.github.com"); 18 | return await github.Content.ReadAsStringAsync(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/BlazorWorker.Demo.SharedPages.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0;net8.0 5 | Latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Pages/Http.razor: -------------------------------------------------------------------------------- 1 | @inject IWorkerFactory workerFactory 2 | 3 |
4 |
5 |

Using HttpClient

6 | 7 | This demo calls the github api from a new thread. 8 | 9 |

10 | 11 |
12 |
13 |
14 | Output: 15 |
16 |
17 | @output
18 | 
19 |
20 |
21 | 22 |
23 |
24 | @code { 25 | string output; 26 | IWorker worker; 27 | IWorkerBackgroundService backgroundService; 28 | string canDisposeWorker => worker == null ? null : "disabled"; 29 | string canDisposeService => backgroundService == null ? null : "disabled"; 30 | string RunDisabled => Running ? "disabled" : null; 31 | bool Running = false; 32 | private string rn = Environment.NewLine; 33 | 34 | public async Task OnClick(EventArgs _) 35 | { 36 | Running = true; 37 | output = ""; 38 | try 39 | { 40 | 41 | if (worker == null) 42 | { 43 | worker = await workerFactory.CreateAsync(); 44 | } 45 | 46 | var sw = new System.Diagnostics.Stopwatch(); 47 | if (backgroundService == null) 48 | { 49 | output = $"{rn}{LogDate()} Creating background service..."; 50 | StateHasChanged(); 51 | 52 | sw.Start(); 53 | backgroundService = await worker.CreateBackgroundServiceAsync( 54 | /*options => options 55 | .AddConventionalAssemblyOfService() 56 | .AddHttpClient()*/ 57 | ); 58 | 59 | sw.Stop(); 60 | output += $"{rn}{LogDate()} Background service created in {sw.ElapsedMilliseconds}ms"; 61 | StateHasChanged(); 62 | } 63 | 64 | output += $"{rn}{LogDate()} Calling Github WebService..."; 65 | var result = await backgroundService.RunAsync(s => s.CallGitHubApi()); 66 | 67 | output += $"{rn}{LogDate()} Result:{rn}{FormatJson(result)}"; 68 | StateHasChanged(); 69 | 70 | } 71 | catch (Exception e) 72 | { 73 | output += $"{rn}Error = {e}"; 74 | } 75 | finally 76 | { 77 | Running = false; 78 | } 79 | } 80 | 81 | private string LogDate() 82 | { 83 | return DateTime.Now.ToString("HH:mm:ss:fff"); 84 | } 85 | 86 | private static string FormatJson(string json) 87 | { 88 | var parsedJson = Newtonsoft.Json.JsonConvert.DeserializeObject(json); 89 | return Newtonsoft.Json.JsonConvert.SerializeObject(parsedJson, Newtonsoft.Json.Formatting.Indented); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Pages/JsDirect.razor: -------------------------------------------------------------------------------- 1 | @inject JsDirectExample jsDirectExample 2 | 3 | 4 |
5 |
6 |

JSDirect Calls

7 | 8 | Demonstrates how to receive messages from a dotnet worker on the main/ui js thread directly. 9 | Also demonstrates putting most of the logic in a separate class (JsDirectExample) 10 |
11 |
12 |

13 |
14 |
15 |
16 |
17 | JsDirect.razor Output: 18 | 19 |
20 |
@output
21 |
22 |
23 | Main js Output: 24 |
25 |

26 |             
27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 | @code { 35 | string output; 36 | string RunDisabled => Running ? "disabled" : null; 37 | bool Running = false; 38 | 39 | protected override void OnInitialized() 40 | { 41 | jsDirectExample.LogHandler += (s, e) => log(e); 42 | output = ""; 43 | base.OnInitialized(); 44 | } 45 | 46 | public async Task OnClick(EventArgs _) 47 | { 48 | Running = true; 49 | try 50 | { 51 | await jsDirectExample.Execute(); 52 | } 53 | catch (Exception e) 54 | { 55 | log($"Error = {e}"); 56 | } 57 | finally 58 | { 59 | Running = false; 60 | } 61 | } 62 | 63 | void log(string logStr){ 64 | output += $"{Environment.NewLine}{LogDate()} {logStr}"; 65 | StateHasChanged(); 66 | } 67 | 68 | private string LogDate() 69 | { 70 | return DateTime.Now.ToString("HH:mm:ss:fff"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Pages/JsInteractions.razor: -------------------------------------------------------------------------------- 1 | @inject JsInteractionsExample jsDirectExample 2 | 3 |
4 |
5 |

Interacting with Js Calls

6 | 7 | Demonstrates how to call to js from a worker using IJsRuntime, 8 | and how call back directly to a dotnet instance. 9 | 10 | 11 |
12 |
13 |

14 |
15 |
16 |
17 |
18 | JsInteractions.razor Output: 19 | 20 |
21 |
@output
22 |
23 |
24 | Worker dotnet Output: 25 |
26 |
@workeroutput
27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 | @code { 36 | string output; 37 | string workeroutput; 38 | string RunDisabled => Running ? "disabled" : null; 39 | bool Running = false; 40 | 41 | protected override void OnInitialized() 42 | { 43 | jsDirectExample.LogHandler += (s, e) => log(e); 44 | jsDirectExample.WorkerLogHandler += (s, e) => workerlog(e); 45 | output = ""; 46 | workeroutput = ""; 47 | base.OnInitialized(); 48 | } 49 | 50 | public async Task OnClick(EventArgs _) 51 | { 52 | Running = true; 53 | try 54 | { 55 | await jsDirectExample.Execute(); 56 | } 57 | catch (Exception e) 58 | { 59 | log($"Error = {e}"); 60 | } 61 | finally 62 | { 63 | Running = false; 64 | } 65 | } 66 | 67 | void log(string logStr){ 68 | output += $"{Environment.NewLine}{LogDate()} {logStr}"; 69 | StateHasChanged(); 70 | } 71 | 72 | void workerlog(string logStr) 73 | { 74 | workeroutput += $"{Environment.NewLine}{LogDate()} {logStr}"; 75 | StateHasChanged(); 76 | } 77 | 78 | private string LogDate() 79 | { 80 | return DateTime.Now.ToString("HH:mm:ss:fff"); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Shared/CustomExpressionSerializer.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerBackgroundService; 2 | using Serialize.Linq.Serializers; 3 | using System; 4 | using System.Linq.Expressions; 5 | using static BlazorWorker.Demo.SharedPages.Pages.ComplexSerialization; 6 | 7 | namespace BlazorWorker.Demo.SharedPages.Shared 8 | { 9 | /// 10 | /// Example 1: Simple custom expression Serializer using 11 | /// as base class, but explicitly adds complex types as known types. 12 | /// 13 | public class CustomSerializeLinqExpressionJsonSerializer : SerializeLinqExpressionJsonSerializerBase 14 | { 15 | public override Type[] GetKnownTypes() => 16 | [typeof(ComplexServiceArg), typeof(ComplexServiceResponse), typeof(OhLookARecord)]; 17 | } 18 | 19 | /// 20 | /// Fully custom Expression Serializer, which uses but you could use an alternative implementation. 21 | /// 22 | public class CustomExpressionSerializer : IExpressionSerializer 23 | { 24 | private readonly ExpressionSerializer serializer; 25 | 26 | public CustomExpressionSerializer() 27 | { 28 | var specificSerializer = new JsonSerializer(); 29 | specificSerializer.AddKnownType(typeof(ComplexServiceArg)); 30 | specificSerializer.AddKnownType(typeof(ComplexServiceResponse)); 31 | specificSerializer.AddKnownType(typeof(OhLookARecord)); 32 | 33 | this.serializer = new ExpressionSerializer(specificSerializer); 34 | } 35 | 36 | public Expression Deserialize(string expressionString) 37 | { 38 | return serializer.DeserializeText(expressionString); 39 | } 40 | 41 | public string Serialize(Expression expression) 42 | { 43 | return serializer.SerializeText(expression); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Shared/GithubSource.razor: -------------------------------------------------------------------------------- 1 | Source for this page
2 | @RelativePath 3 |
4 | 5 | @code { 6 | private const string Tag = "v4.1.2"; 7 | private static readonly string RawRoot = $"https://rawcdn.githack.com/Tewr/BlazorWorker/{Tag}/src/BlazorWorker.Demo/SharedPages/"; 8 | private static readonly string Root = $"https://github.com/Tewr/BlazorWorker/blob/{Tag}/src/BlazorWorker.Demo/SharedPages/"; 9 | 10 | [Parameter] 11 | public string RelativePath { get; set; } 12 | 13 | public string FullRawUrl => RawRoot + RelativePath; 14 | public string FullUrl => Root + RelativePath; 15 | } 16 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 | 6 | 7 |
8 |
9 | @Body 10 |
11 |
12 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Shared/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  7 | 8 |
9 | 16 |
17 | 18 | @code { 19 | private bool collapseNavMenu = true; 20 | 21 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; 22 | 23 | private void ToggleNavMenu() 24 | { 25 | collapseNavMenu = !collapseNavMenu; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Shared/NavMenuLink.razor: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | @if (Model.Small) 16 | { 17 | @(Model.Text) 18 | } 19 | else 20 | { 21 | @(Model.Text) 22 | } 23 | 24 | @code { 25 | [Parameter] 26 | public NavMenuLinkInfo Model { get; set; } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/Shared/NavMenuLinksModel.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Demo.SharedPages.Pages; 2 | using Microsoft.AspNetCore.Components; 3 | using Microsoft.AspNetCore.Components.Routing; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace BlazorWorker.Demo.SharedPages.Shared 9 | { 10 | public class NavMenuLinksModel 11 | { 12 | static string BlazorWorkerVersion { get; } = 13 | $"v{typeof(BlazorWorker.BackgroundServiceFactory.WorkerBackgroundServiceExtensions).Assembly.GetName().Version}"; 14 | 15 | static string BlazorCoreWorkerVersion { get; } = 16 | $"v{typeof(BlazorWorker.WorkerCore.IWorkerMessageService).Assembly.GetName().Version}"; 17 | 18 | public static IEnumerable NavMenuLinks { get; } = new List() 19 | { 20 | { 21 | new() { Icon = "cog", Href="", Text = "Simple Worker", Match= NavLinkMatch.All } 22 | }, 23 | { 24 | new() { Icon = "copywriting", Href="BackgroundServiceMulti", Text = "Multiple Workers" } 25 | }, 26 | { 27 | new() { Icon = "command", Href="CoreExample", Text = "Core Example" } 28 | }, 29 | { 30 | new() { Icon = "globe", Href="Http", Text = "HttpClient Example" } 31 | }, 32 | { 33 | new() { Icon = "transfer", Href="IoCExample", Text = "IoC / DI Example" } 34 | }, 35 | { 36 | new() { Icon = "document", Href="IndexedDb", Text = "IndexedDB" } 37 | }, 38 | { 39 | new() { Icon = "document", Href="ComplexSerialization", Text = "ComplexSerialization" } 40 | }, 41 | { 42 | new() { Icon = "document", Href="JsDirect", Text = "JsDirect" } 43 | }, 44 | { 45 | new() { Icon = "document", Href="JsInteractions", Text = "JsInteractions" } 46 | }, 47 | { 48 | new() { Icon = "fork", Href="https://github.com/tewr/BlazorWorker", Text = "To the source!" } 49 | }, 50 | { 51 | new() { Icon = "info", Href="https://www.nuget.org/packages/Tewr.BlazorWorker.BackgroundService", Small=true, Text = $"BackgroundService {BlazorWorkerVersion}" } 52 | }, 53 | { 54 | new() { Icon = "info", Href="https://www.nuget.org/packages/Tewr.BlazorWorker.Core", Small=true, Text = $"Core {BlazorCoreWorkerVersion}" } 55 | } 56 | }; 57 | } 58 | 59 | public class NavMenuLinkInfo 60 | { 61 | 62 | public string Icon { get; set; } 63 | 64 | 65 | public string Href { get; set; } 66 | 67 | public bool Small { get; set; } 68 | 69 | 70 | public NavLinkMatch Match { get; set; } = NavLinkMatch.Prefix; 71 | public string Text { get; internal set; } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.JSInterop 6 | @using BlazorWorker.Demo.SharedPages 7 | @using BlazorWorker.Demo.SharedPages.Shared 8 | @using BlazorWorker.Demo.Shared 9 | @using BlazorWorker.BackgroundServiceFactory 10 | @using BlazorWorker.WorkerBackgroundService 11 | @using BlazorWorker.Core -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/wwwroot/JsDirectExample.js: -------------------------------------------------------------------------------- 1 | // This 'classic' script will run on the main thread and cirectly receieve message from the worker 2 | function setupJsDirectForWorker(myWorkerId) { 3 | const currtime = () => [new Date()].map(d => `${d.toTimeString().split(" ")[0]}:${d.getMilliseconds()}`)[0]; 4 | const output = document.getElementById('jsDirectOutputElement'); 5 | output.innerText += `\n${currtime()} Setting up event listener.`; 6 | window.addEventListener('blazorworker:jsdirect', function (e) { 7 | if (e.detail.workerId === myWorkerId) { 8 | output.innerText += `\n${currtime()} blazorworker:jsdirect listener. workerId: ${e.detail.workerId}. data: '${e.detail.data}'`; 9 | } 10 | else { 11 | console.log('blazorworker:jsdirect handler for some other worker not handled by this listener', { workerId: e.detail.workerId, data: e.detail.data }); 12 | } 13 | }); 14 | } -------------------------------------------------------------------------------- /src/BlazorWorker.Demo/SharedPages/wwwroot/JsInteractionsExample.js: -------------------------------------------------------------------------------- 1 | // This script will run on the worker. 2 | self.jsInteractionsExample = async (JsInteractionsExampleWorkerService) => { 3 | console.log(`Interacting with worker Js on worker thread.`); 4 | await JsInteractionsExampleWorkerService.invokeMethodAsync("CallbackFromJavascript", 'Callback to dotnet instance method'); 5 | 6 | // Calling static methods can be done like this 7 | var demoExports = await self.BlazorWorker.getAssemblyExports("BlazorWorker.Demo.Shared") 8 | var methodResult = demoExports.BlazorWorker.Demo.Shared.JsInteractionsExampleWorkerService.StaticCallbackFromJs("Static methods are cheaper to call."); 9 | 10 | await JsInteractionsExampleWorkerService.invokeMethodAsync("CallbackFromJavascript", 'StaticCallbackFromJs returned: '+ methodResult); 11 | } -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/BlazorWorker.Extensions.JSRuntime.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0;net8.0 5 | Tor Knutsson (Tewr) 6 | BlazorWorker 7 | MIT 8 | Extension for BlazorWorker.Core -- use IJSRuntime in dotnet Web Workers Threads in Blazor 9 | https://github.com/Tewr/BlazorWorker 10 | https://github.com/Tewr/BlazorWorker 11 | WebWorker Worker Process Threading Multithreading Blazor Isolation 12 | Tewr.BlazorWorker.Extensions.JSRuntime 13 | Debug;Release;Nuget 14 | 4.1.3 15 | 4.1.3.0 16 | BlazorWorker.Extensions.JSRuntime.xml 17 | icon.png 18 | false 19 | Fix a bug related to .net instance callback from worker thread js 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | true 46 | 47 | 48 | 49 | 1701;1702;1591;CA1416 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/BlazorWorkerJsRuntimeSetupExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.JSInterop; 3 | using System; 4 | using System.Text.Json; 5 | 6 | namespace BlazorWorker.Extensions.JSRuntime 7 | { 8 | public static class BlazorWorkerJsRuntimeSetupExtensions 9 | { 10 | public static IServiceCollection AddBlazorWorkerJsRuntime(this IServiceCollection source, Action optionsModifier = null) 11 | { 12 | source.AddSingleton(CreateBlazorWorkerJSRuntime(optionsModifier)); 13 | return source; 14 | } 15 | 16 | private static Func CreateBlazorWorkerJSRuntime(Action optionsModifier) { 17 | 18 | var instance = new BlazorWorkerJSRuntime(); 19 | 20 | if (optionsModifier != null) 21 | { 22 | optionsModifier(instance.SerializerOptions); 23 | } 24 | 25 | return _ => instance; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/DefaultBlazorWorkerJSRuntimeSerializer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Text.Json; 3 | 4 | namespace BlazorWorker.Extensions.JSRuntime 5 | { 6 | internal class DefaultBlazorWorkerJSRuntimeSerializer : IBlazorWorkerJSRuntimeSerializer 7 | { 8 | private readonly JsonSerializerOptions options; 9 | 10 | public DefaultBlazorWorkerJSRuntimeSerializer(JsonSerializerOptions options) 11 | { 12 | this.options = options; 13 | } 14 | 15 | public T Deserialize(string serializedObject) 16 | { 17 | return System.Text.Json.JsonSerializer.Deserialize(serializedObject, options); 18 | } 19 | 20 | public string Serialize(object obj) 21 | { 22 | return System.Text.Json.JsonSerializer.Serialize(obj, options); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/DotNetObjectReferenceJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace BlazorWorker.Extensions.JSRuntime 7 | { 8 | internal sealed class DotNetObjectReferenceJsonConverter : JsonConverter> where TValue : class 9 | { 10 | public DotNetObjectReferenceJsonConverter(BlazorWorkerJSRuntime jsRuntime) 11 | { 12 | CallbackJSRuntime = jsRuntime; 13 | } 14 | 15 | public BlazorWorkerJSRuntime CallbackJSRuntime { get; } 16 | 17 | public override DotNetObjectReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 18 | { 19 | long dotNetObjectId = 0; 20 | 21 | while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) 22 | { 23 | if (reader.TokenType == JsonTokenType.PropertyName) 24 | { 25 | if (dotNetObjectId == 0 && reader.ValueTextEquals(DotNetObjectReferenceTracker.DotNetObjectRefKey.EncodedUtf8Bytes)) 26 | { 27 | reader.Read(); 28 | dotNetObjectId = reader.GetInt64(); 29 | } 30 | else 31 | { 32 | throw new JsonException($"Unexcepted JSON property {reader.GetString()}."); 33 | } 34 | } 35 | else 36 | { 37 | throw new JsonException($"Unexcepted JSON Token {reader.TokenType}."); 38 | } 39 | } 40 | 41 | if (dotNetObjectId is 0) 42 | { 43 | throw new JsonException($"Required property {DotNetObjectReferenceTracker.DotNetObjectRefKey} not found."); 44 | } 45 | 46 | var value = DotNetObjectReferenceTracker.GetObjectReference(dotNetObjectId); 47 | return value; 48 | } 49 | 50 | public override void Write(Utf8JsonWriter writer, DotNetObjectReference value, JsonSerializerOptions options) 51 | { 52 | DotNetObjectReferenceTracker.SetCallbackJSRuntime(value, CallbackJSRuntime); 53 | var objectId = DotNetObjectReferenceTracker.TrackObjectReference(value); 54 | 55 | writer.WriteStartObject(); 56 | writer.WriteNumber(DotNetObjectReferenceTracker.DotNetObjectRefKey, objectId); 57 | 58 | writer.WriteEndObject(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/DotNetObjectReferenceJsonConverterFactory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.JSInterop; 5 | using System; 6 | using System.Text.Json; 7 | using System.Text.Json.Serialization; 8 | 9 | namespace BlazorWorker.Extensions.JSRuntime 10 | { 11 | internal sealed class DotNetObjectReferenceJsonConverterFactory : JsonConverterFactory 12 | { 13 | public DotNetObjectReferenceJsonConverterFactory(IJSRuntime jsRuntime) 14 | { 15 | JSRuntime = jsRuntime; 16 | } 17 | 18 | public IJSRuntime JSRuntime { get; private set; } 19 | 20 | public override bool CanConvert(Type typeToConvert) 21 | { 22 | return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DotNetObjectReference<>); 23 | } 24 | 25 | public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) 26 | { 27 | // System.Text.Json handles caching the converters per type on our behalf. No caching is required here. 28 | var instanceType = typeToConvert.GetGenericArguments()[0]; 29 | var converterType = typeof(DotNetObjectReferenceJsonConverter<>).MakeGenericType(instanceType); 30 | 31 | return (JsonConverter)Activator.CreateInstance(converterType, JSRuntime)!; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/GenericNonPublicDelegateCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace BlazorWorker.Extensions.JSRuntime 7 | { 8 | 9 | public class GenericNonPublicDelegateCache : ConcurrentDictionary 10 | { 11 | public GenericNonPublicDelegateCache(Type forType) 12 | { 13 | TargetType = forType; 14 | } 15 | 16 | public Type TargetType { get; } 17 | 18 | public TDelegate GetDelegate(object definingTypeInstance, string name) where TDelegate : Delegate 19 | { 20 | var method = this.GetOrAdd(typeof(T), firstGenericTypeArg => { 21 | 22 | var firstArgGenericDef = typeof(TFirstArg).GetGenericTypeDefinition(); 23 | var methodInfo = TargetType.GetRuntimeMethods().FirstOrDefault(methodInfo => 24 | !methodInfo.IsPublic && 25 | methodInfo.Name == name && 26 | methodInfo.ContainsGenericParameters && 27 | AreGenericTypeEquals(methodInfo.GetParameters().FirstOrDefault()?.ParameterType, firstArgGenericDef)); 28 | if (methodInfo == null) 29 | { 30 | throw new ArgumentException($"Unable to find non-public method {definingTypeInstance}.{name}({firstArgGenericDef})"); 31 | } 32 | 33 | var genericMethodInfo = methodInfo.MakeGenericMethod(typeof(T)); 34 | 35 | return Delegate.CreateDelegate(typeof(TDelegate), definingTypeInstance, genericMethodInfo); 36 | }); 37 | 38 | return (TDelegate)method; 39 | } 40 | 41 | 42 | private bool AreGenericTypeEquals(Type genericType1, Type genericType2) 43 | { 44 | return genericType1.Assembly == genericType2.Assembly && 45 | genericType1.Namespace == genericType2.Namespace && 46 | genericType1.Name == genericType2.Name; 47 | } 48 | } 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/IBlazorWorkerJSRuntimeSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BlazorWorker.Extensions.JSRuntime 6 | { 7 | public interface IBlazorWorkerJSRuntimeSerializer 8 | { 9 | public string Serialize(object obj); 10 | 11 | T Deserialize(string serializedObject); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/JSInvokeService.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices.JavaScript; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.Extensions.JSRuntime 5 | { 6 | public partial class JSInvokeService 7 | { 8 | /// 9 | /// Imports locally hosted module scripts 10 | /// 11 | /// 12 | /// 13 | public static Task ImportLocalScripts(params string[] relativeUrls) 14 | => PrivateImportLocalScripts(relativeUrls); 15 | 16 | /// 17 | /// Invokes a method defined on the worker globalThis / self object 18 | /// 19 | /// 20 | /// 21 | /// JSON serialized parameters 22 | /// 23 | public static string WorkerInvoke(string method, string args) 24 | => (string) PrivateWorkerInvoke(method, args); 25 | 26 | /// 27 | /// Asynchronically Invokes a method defined on the worker globalThis / self object 28 | /// 29 | /// 30 | /// 31 | /// JSON serialized parameters 32 | /// 33 | public static async Task WorkerInvokeAsync(string method, string args) 34 | => (await PrivateWorkerInvokeAsync(method, args)); 35 | 36 | #region Generated methods 37 | 38 | /// 39 | /// Checks if the specified object path is defined using the self / globalThis object as root. 40 | /// 41 | /// 42 | /// 43 | [JSImport("IsObjectDefined", "BlazorWorker.js")] 44 | public static partial bool IsObjectDefined(string objectPath); 45 | 46 | /// 47 | /// Prepending the specified with the base path of the application, invokes the importScripts() method of the WorkerGlobalScope interface, which synchronously imports one or more scripts into the worker's scope. 48 | /// 49 | /// 50 | [JSImport("ImportLocalScripts", "BlazorWorker.js")] 51 | private static partial Task PrivateImportLocalScripts(string[] relativeUrls); 52 | 53 | [JSImport("WorkerInvokeAsync", "BlazorWorkerJSRuntime.js")] 54 | private static partial Task PrivateWorkerInvokeAsync(string method, string args); 55 | 56 | [JSImport("WorkerInvoke", "BlazorWorkerJSRuntime.js")] 57 | private static partial string PrivateWorkerInvoke(string method, string args); 58 | 59 | #endregion 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.Extensions.JSRuntime/icon.png -------------------------------------------------------------------------------- /src/BlazorWorker.Extensions.JSRuntime/wwwroot/BlazorWorkerJSRuntime.js: -------------------------------------------------------------------------------- 1 | class BlazorWorkerBaseSerializer { 2 | serialize(o) { return JSON.stringify(o); } 3 | deserialize(s) {return JSON.parse(s); } 4 | } 5 | 6 | class DotNetObjectProxy { 7 | constructor(id) { 8 | this.__dotNetObject = `${id}`; 9 | this.serializer = self.jsRuntimeSerializer || new BlazorWorkerBaseSerializer(); 10 | } 11 | 12 | async invokeMethodAsync(methodName, ...methodArgs) { 13 | if (this.blazorWorkerJSRuntime === undefined) { 14 | const exports = await self.BlazorWorker.getAssemblyExports("BlazorWorker.Extensions.JSRuntime"); 15 | this.blazorWorkerJSRuntime = exports.BlazorWorker.Extensions.JSRuntime.BlazorWorkerJSRuntime; 16 | } 17 | 18 | return await new Promise((resolve, reject) => { 19 | try { 20 | const argsString = this.serializer.serialize({ 21 | methodName, 22 | methodArgs : methodArgs || [] 23 | }); 24 | var result = this.blazorWorkerJSRuntime.InvokeMethod(this.__dotNetObject, argsString); 25 | resolve(result); 26 | } catch (e) { 27 | reject(e); 28 | } 29 | }); 30 | } 31 | } 32 | 33 | class BlazorWorkerJSRuntimeSerializer { 34 | 35 | constructor() { 36 | this.baseSerializer = new BlazorWorkerBaseSerializer(); 37 | } 38 | 39 | serialize = (o) => this.baseSerializer.serialize(o); 40 | 41 | deserialize = (s) => { 42 | let deserializedObj = this.baseSerializer.deserialize(s); 43 | deserializedObj = BlazorWorkerJSRuntimeSerializer.recursivelyFindDotnetObjectProxy(deserializedObj); 44 | return deserializedObj; 45 | } 46 | 47 | static recursivelyFindDotnetObjectProxy(obj) { 48 | 49 | const recursion = BlazorWorkerJSRuntimeSerializer.recursivelyFindDotnetObjectProxy; 50 | const dotnetObjectKey = "__dotNetObject"; 51 | const keys = Object.keys(obj); 52 | if (keys.length === 1 && keys[0] === dotnetObjectKey) { 53 | return new DotNetObjectProxy(obj[dotnetObjectKey]); 54 | } 55 | 56 | for (let i = 0; i < keys.length; i++) { 57 | const property = keys[i]; 58 | let value = obj[property]; 59 | if (value !== null && typeof value === "object") { 60 | obj[property] = recursion(value); 61 | } 62 | } 63 | 64 | return obj; 65 | } 66 | }; 67 | 68 | const serializer = new BlazorWorkerJSRuntimeSerializer(); 69 | 70 | const workerInvokeAsync = async function (method, argsString) { 71 | 72 | const methodHandle = self.BlazorWorker.getChildFromDotNotation(method); 73 | 74 | if (methodHandle === self.BlazorWorker.empty) { 75 | throw new Error(`workerInvokeAsync: Method '${method}' not defined`); 76 | } 77 | 78 | const argsArray = serializer.deserialize(argsString); 79 | const result = await methodHandle(...argsArray); 80 | return serializer.serialize(result); 81 | } 82 | 83 | const workerInvoke = function (method, argsString) { 84 | 85 | const methodHandle = self.BlazorWorker.getChildFromDotNotation(method); 86 | if (methodHandle === self.BlazorWorker.empty) { 87 | throw new Error(`workerInvoke: Method '${method}' not defined`); 88 | } 89 | 90 | const argsArray = serializer.deserialize(argsString); 91 | const result = methodHandle(...argsArray); 92 | return serializer.serialize(result); 93 | } 94 | 95 | self.BlazorWorker.setModuleImports("BlazorWorkerJSRuntime.js", { 96 | WorkerInvokeAsync: workerInvokeAsync, 97 | WorkerInvoke: workerInvoke 98 | }); -------------------------------------------------------------------------------- /src/BlazorWorker.ServiceFactory/BackgroundServiceWorkerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | namespace BlazorWorker.BackgroundServiceFactory 4 | { 5 | 6 | public class BackgroundServiceWorkerException: Exception 7 | { 8 | private readonly string workerExceptionString; 9 | private string _stacktrace; 10 | 11 | public long WorkerId { get; } 12 | 13 | public BackgroundServiceWorkerException(long workerId, string message, string workerExceptionString) : base(message) 14 | { 15 | this.WorkerId = workerId; 16 | this.workerExceptionString = workerExceptionString; 17 | } 18 | 19 | public override string StackTrace => (this._stacktrace ??= BuildStackTrace()); 20 | 21 | private string BuildStackTrace() 22 | { 23 | var stack = new StringBuilder(); 24 | 25 | stack.AppendLine(this.workerExceptionString); 26 | stack.AppendLine($" --- Worker Process Border (WorkerId: {this.WorkerId}) ---"); 27 | stack.AppendLine(base.StackTrace); 28 | 29 | return stack.ToString(); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/BlazorWorker.ServiceFactory/BlazorWorker.BackgroundServiceFactory.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0;net8.0 5 | Tewr.BlazorWorker.BackgroundService 6 | MIT 7 | Use dotnet Web Workers Threads in Blazor 8 | https://github.com/Tewr/BlazorWorker 9 | https://github.com/Tewr/BlazorWorker 10 | WebWorker Worker Process Threading Multithreading Blazor Isolation 11 | Tewr 12 | BlazorWorker 13 | Debug;Release;Nuget 14 | 4.1.2 15 | 4.1.2.0 16 | icon.png 17 | false 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | all 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | all 51 | 52 | 53 | 54 | 55 | 56 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage 57 | 58 | true 59 | 60 | 61 | 62 | 63 | BlazorWorker.BackgroundServiceFactory.xml 64 | 65 | 66 | 67 | 1701;1702;1591;CA1416 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/BlazorWorker.ServiceFactory/BlazorWorker.BackgroundServiceFactory.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BlazorWorker.BackgroundServiceFactory 5 | 6 | 7 | 8 | 9 | Creates a background service using the specified . 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Creates a new background service using the specified 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Attached objects, notably parent worker proxy which may have been created without consumer being directly able to dispose 31 | 32 | 33 | 34 | 35 | Sets a custom ExpressionSerializer type. Must implement .. 36 | 37 | 38 | A type that implements 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/BlazorWorker.ServiceFactory/WorkerInitExtension.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Core; 2 | using BlazorWorker.WorkerBackgroundService; 3 | using System; 4 | 5 | namespace BlazorWorker.BackgroundServiceFactory 6 | { 7 | public static class WorkerInitExtension 8 | { 9 | 10 | /// 11 | /// Sets a custom ExpressionSerializer type. Must implement .. 12 | /// 13 | /// 14 | /// A type that implements 15 | /// 16 | /// 17 | public static WorkerInitOptions UseCustomExpressionSerializer(this WorkerInitOptions source, Type expressionSerializerType) 18 | { 19 | if (source == null) 20 | { 21 | throw new ArgumentNullException(nameof(source)); 22 | } 23 | 24 | source.SetEnv(WebWorkerOptions.ExpressionSerializerTypeEnvKey, expressionSerializerType.AssemblyQualifiedName); 25 | return source; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/BlazorWorker.ServiceFactory/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker.ServiceFactory/icon.png -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/AssemblyInfo1.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // In SDK-style projects such as this one, several assembly attributes that were historically 4 | // defined in this file are now automatically added during build and populated with 5 | // values defined in project properties. For details of which attributes are included 6 | // and how to customise this process see: https://aka.ms/assembly-info-properties 7 | 8 | 9 | // Setting ComVisible to false makes the types in this assembly not visible to COM 10 | // components. If you need to access a type in this assembly from COM, set the ComVisible 11 | // attribute to true on that type. 12 | 13 | [assembly: ComVisible(false)] 14 | 15 | // The following GUID is for the ID of the typelib if this project is exposed to COM. 16 | 17 | [assembly: Guid("738e7519-3b77-4ecc-a639-3c47fa4412fd")] 18 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/BaseMessage.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class BaseMessage 4 | { 5 | public string MessageType { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/BlazorWorker.WorkerBackgroundService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0;net8.0 5 | 6 | Debug;Release;Nuget 7 | 4.1.0 8 | 4.1.0.0 9 | true 10 | 11 | 12 | 13 | BlazorWorker.WorkerBackgroundService.xml 14 | 15 | 16 | 17 | 1701;1702;1591;CA1416 18 | 19 | 20 | 21 | 22 | $(MSBuildProjectName).xml 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/DefaultMessageSerializer.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public class DefaultMessageSerializer : ISerializer 6 | { 7 | public T Deserialize(string objStr) 8 | { 9 | return JsonConvert.DeserializeObject(objStr); 10 | } 11 | 12 | public string Serialize(object obj) 13 | { 14 | return JsonConvert.SerializeObject(obj); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/DisposeInstance.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class DisposeInstance : BaseMessage 4 | { 5 | public DisposeInstance() 6 | { 7 | MessageType = nameof(DisposeInstance); 8 | } 9 | 10 | public long InstanceId { get; set; } 11 | 12 | public long CallId { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/DisposeInstanceComplete.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public class DisposeInstanceComplete : BaseMessage 6 | { 7 | public DisposeInstanceComplete() 8 | { 9 | MessageType = nameof(DisposeInstanceComplete); 10 | } 11 | 12 | public long CallId { get; set; } 13 | 14 | public bool IsSuccess { get; set; } 15 | 16 | public Exception Exception { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/EventHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BlazorWorker.WorkerBackgroundService 6 | { 7 | public class EventHandle 8 | { 9 | public delegate void HandlePayloadMessage(string payLoad); 10 | 11 | private static long idSource; 12 | public EventHandle() 13 | { 14 | Id = ++idSource; 15 | } 16 | public long Id { get; } 17 | 18 | public HandlePayloadMessage EventHandler { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/EventHandlerWrapper.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerBackgroundService; 2 | using System; 3 | 4 | namespace BlazorWorker.WorkerBackgroundService 5 | { 6 | public class EventHandlerWrapper : IEventWrapper 7 | { 8 | private readonly WorkerInstanceManager wim; 9 | 10 | public EventHandlerWrapper( 11 | WorkerInstanceManager wim, 12 | long instanceId, 13 | long eventHandleId) 14 | { 15 | this.wim = wim; 16 | InstanceId = instanceId; 17 | EventHandleId = eventHandleId; 18 | } 19 | 20 | public long InstanceId { get; } 21 | public long EventHandleId { get; } 22 | 23 | public Action Unregister { get; set; } 24 | 25 | public void OnEvent(object _, T eventArgs) 26 | { 27 | wim.PostObject(new EventRaised() 28 | { 29 | EventHandleId = EventHandleId, 30 | InstanceId = InstanceId, 31 | ResultPayload = wim.serializer.Serialize(eventArgs) 32 | }); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/EventRaised.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class EventRaised : BaseMessage 4 | { 5 | public EventRaised() 6 | { 7 | MessageType = nameof(EventRaised); 8 | } 9 | 10 | public long EventHandleId { get; set; } 11 | 12 | public long InstanceId { get; set; } 13 | 14 | public string ResultPayload { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/IEventWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public interface IEventWrapper 6 | { 7 | long InstanceId { get; } 8 | long EventHandleId { get; } 9 | Action Unregister { get; set; } 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/IExpressionSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public interface IExpressionSerializer 6 | { 7 | string Serialize(Expression expr); 8 | 9 | Expression Deserialize(string exprString); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/ISerializer.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public interface ISerializer 4 | { 5 | string Serialize(object obj); 6 | 7 | T Deserialize(string objStr); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/IWorkerBackgroundService.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorWorker.WorkerBackgroundService 8 | { 9 | public interface IWorkerBackgroundService : IAsyncDisposable where T : class 10 | { 11 | /// 12 | /// Registers an event listener to the specified event. 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | Task RegisterEventListenerAsync(string eventName, EventHandler myHandler); 19 | 20 | /// 21 | /// Queues the specified work to run on the underlying worker and returns a object that represents that work. 22 | /// 23 | /// 24 | /// 25 | /// 26 | Task RunAsync(Expression> function); 27 | 28 | /// 29 | /// Queues the specified work to run on the underlying worker and returns a object that represents that work. 30 | /// 31 | /// 32 | /// 33 | Task RunAsync(Expression> action); 34 | 35 | /// 36 | /// Queues the specified awaitable work to run on the underlying worker, awaits the result, and returns a object that represents that work. 37 | /// 38 | /// 39 | /// 40 | /// 41 | Task RunAsync(Expression>> function); 42 | 43 | /// 44 | /// Queues the specified awaitable work to run on the underlying worker, awaits the result, and returns a object that represents that work. 45 | /// 46 | /// 47 | /// 48 | Task RunAsync(Expression> function); 49 | 50 | /// 51 | /// Returns the message service used by the underlying worker. 52 | /// 53 | /// 54 | IWorkerMessageService GetWorkerMessageService(); 55 | 56 | /// 57 | /// Unregisters the event corresponding to the specified 58 | /// 59 | Task UnRegisterEventListenerAsync(EventHandle handle); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/IWorkerBackgroundServiceFactory.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Core; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.WorkerBackgroundService 5 | { 6 | public interface IWorkerBackgroundServiceFactory 7 | { 8 | Task CreateWebworkerAsync(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/InitInstance.cs: -------------------------------------------------------------------------------- 1 | using System.Net.WebSockets; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public class InitInstance : BaseMessage 6 | { 7 | public InitInstance() 8 | { 9 | MessageType = nameof(InitInstance); 10 | } 11 | 12 | public long WorkerId { get; set; } 13 | public long InstanceId { get; set; } 14 | public string AssemblyName { get; set; } 15 | public string TypeName { get; set; } 16 | 17 | public long CallId { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/InitInstanceComplete.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public class InitInstanceComplete : BaseMessage 6 | { 7 | public InitInstanceComplete() 8 | { 9 | MessageType = nameof(InitInstanceComplete); 10 | } 11 | 12 | public long CallId { get; set; } 13 | 14 | public bool IsSuccess { get; set; } 15 | 16 | public Exception Exception { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/InitInstanceFromFactory.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class InitInstanceFromFactory : BaseMessage 4 | { 5 | public InitInstanceFromFactory() 6 | { 7 | MessageType = nameof(InitInstanceFromFactory); 8 | } 9 | 10 | public long WorkerId { get; set; } 11 | public long InstanceId { get; set; } 12 | 13 | public long FactoryInstanceId { get; set; } 14 | public string SerializedFactoryExpression { get; set; } 15 | 16 | public long CallId { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/InitInstanceFromFactoryComplete.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public class InitInstanceFromFactoryComplete : BaseMessage 6 | { 7 | public InitInstanceFromFactoryComplete() 8 | { 9 | MessageType = nameof(InitInstanceFromFactoryComplete); 10 | } 11 | 12 | public long CallId { get; set; } 13 | 14 | public bool IsSuccess { get; set; } 15 | 16 | public Exception Exception { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/InitWorkerComplete.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class InitWorkerComplete : BaseMessage 4 | { 5 | public InitWorkerComplete() 6 | { 7 | MessageType = nameof(InitWorkerComplete); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/LinkerConfig.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/MethodCallParams.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class MethodCallParams : BaseMessage 4 | { 5 | public MethodCallParams() 6 | { 7 | MessageType = nameof(MethodCallParams); 8 | } 9 | 10 | public bool AwaitResult { get; set; } 11 | public long InstanceId { get; set; } 12 | public string SerializedExpression { get; set; } 13 | public long WorkerId { get; set; } 14 | public long CallId { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/MethodCallResult.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class MethodCallResult : BaseMessage 4 | { 5 | public MethodCallResult() 6 | { 7 | MessageType = nameof(MethodCallResult); 8 | } 9 | 10 | public string ResultPayload { get; set; } 11 | 12 | public bool IsException { get; set; } 13 | 14 | public string ExceptionString { get; set; } 15 | 16 | public string ExceptionMessage { get; set; } 17 | 18 | public long CallId { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/RegisterEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public class RegisterEvent: BaseMessage 6 | { 7 | public RegisterEvent() 8 | { 9 | MessageType = nameof(RegisterEvent); 10 | } 11 | 12 | public long EventHandleId { get; set; } 13 | 14 | public long InstanceId { get; set; } 15 | public string EventName { get; set; } 16 | public string EventHandlerTypeArg { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/SerializeLinqExpressionJsonSerializerBase.cs: -------------------------------------------------------------------------------- 1 | using Serialize.Linq.Serializers; 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace BlazorWorker.WorkerBackgroundService 6 | { 7 | /// 8 | /// Base class for adding known types to a serializer using . 9 | /// 10 | public abstract class SerializeLinqExpressionJsonSerializerBase : IExpressionSerializer 11 | { 12 | private ExpressionSerializer serializer; 13 | 14 | private ExpressionSerializer Serializer 15 | => this.serializer ?? (this.serializer = GetSerializer()); 16 | 17 | /// 18 | /// Automatically adds known types as array types. If set to true, sets to false. 19 | /// 20 | public bool? AutoAddKnownTypesAsArrayTypes { get; set; } 21 | 22 | /// 23 | /// Automatically adds known types as list types. If set to true, sets to false. 24 | /// 25 | public bool? AutoAddKnownTypesAsListTypes { get; set; } 26 | 27 | public abstract Type[] GetKnownTypes(); 28 | 29 | private ExpressionSerializer GetSerializer() 30 | { 31 | var jsonSerializer = new JsonSerializer(); 32 | foreach (var type in GetKnownTypes()) 33 | { 34 | jsonSerializer.AddKnownType(type); 35 | } 36 | if (this.AutoAddKnownTypesAsArrayTypes.HasValue) { 37 | jsonSerializer.AutoAddKnownTypesAsArrayTypes = this.AutoAddKnownTypesAsArrayTypes.Value; 38 | } 39 | if (this.AutoAddKnownTypesAsListTypes.HasValue) 40 | { 41 | jsonSerializer.AutoAddKnownTypesAsListTypes = this.AutoAddKnownTypesAsListTypes.Value; 42 | } 43 | 44 | return new ExpressionSerializer(jsonSerializer); 45 | } 46 | 47 | public Expression Deserialize(string expressionString) 48 | { 49 | return this.Serializer.DeserializeText(expressionString); 50 | } 51 | 52 | public string Serialize(Expression expression) 53 | { 54 | return this.Serializer.SerializeText(expression); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/SerializeLinqExpressionSerializer.cs: -------------------------------------------------------------------------------- 1 | using Serialize.Linq.Serializers; 2 | using System.Linq.Expressions; 3 | 4 | namespace BlazorWorker.WorkerBackgroundService 5 | { 6 | public class SerializeLinqExpressionSerializer : IExpressionSerializer 7 | { 8 | private readonly ExpressionSerializer serializer; 9 | 10 | public SerializeLinqExpressionSerializer() 11 | { 12 | this.serializer = new ExpressionSerializer(new JsonSerializer()); 13 | } 14 | 15 | public Expression Deserialize(string expressionString) 16 | { 17 | return serializer.DeserializeText(expressionString); 18 | } 19 | 20 | public string Serialize(Expression expression) 21 | { 22 | return serializer.SerializeText(expression); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/UnRegisterEvent.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.WorkerBackgroundService 2 | { 3 | public class UnRegisterEvent : BaseMessage 4 | { 5 | public UnRegisterEvent() 6 | { 7 | MessageType = nameof(UnRegisterEvent); 8 | } 9 | 10 | public long EventHandleId { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerBackgroundService/WebWorkerOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerBackgroundService 4 | { 5 | public class WebWorkerOptions 6 | { 7 | /// 8 | /// Name of environment variable to be used for transferring the serializer typename 9 | /// 10 | public static readonly string ExpressionSerializerTypeEnvKey = "BLAZORWORKER_EXPRESSIONSERIALIZER"; 11 | 12 | private ISerializer messageSerializer; 13 | private IExpressionSerializer expressionSerializer; 14 | private Type expressionSerializerType; 15 | 16 | 17 | 18 | public ISerializer MessageSerializer { 19 | get => messageSerializer ?? (messageSerializer = new DefaultMessageSerializer()); 20 | set => messageSerializer = value; 21 | } 22 | 23 | public IExpressionSerializer ExpressionSerializer { 24 | get => expressionSerializer ?? (expressionSerializer = CreateSerializerInstance()); 25 | set => expressionSerializer = value; 26 | } 27 | 28 | public Type ExpressionSerializerType 29 | { 30 | get => expressionSerializerType ?? typeof(SerializeLinqExpressionSerializer); 31 | set => expressionSerializerType = ValidateExpressionSerializerType(value); 32 | } 33 | 34 | /// 35 | /// Ensures that the provided type implements 36 | /// 37 | /// 38 | /// 39 | private Type ValidateExpressionSerializerType(Type sourceType) 40 | { 41 | if (sourceType == null) 42 | { 43 | return null; 44 | } 45 | 46 | if (!sourceType.IsClass) 47 | { 48 | throw new Exception($"The {nameof(ExpressionSerializerType)} '{sourceType.AssemblyQualifiedName}' must be a class."); 49 | } 50 | 51 | if (!typeof(IExpressionSerializer).IsAssignableFrom(sourceType)) 52 | { 53 | throw new Exception($"The {nameof(ExpressionSerializerType)} '{sourceType.AssemblyQualifiedName}' must be assignable to {nameof(IExpressionSerializer)}"); 54 | } 55 | 56 | return sourceType; 57 | } 58 | 59 | private IExpressionSerializer CreateSerializerInstance() 60 | { 61 | var instance = Activator.CreateInstance(ExpressionSerializerType); 62 | return (IExpressionSerializer)instance; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/BlazorWorker.WorkerCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0;net8.0 5 | Debug;Release;Nuget 6 | 4.2.0 7 | 4.2.0.0 8 | Tor Knutsson (Tewr) 9 | Tor Knutsson (Tewr) 10 | BlazorWorker 11 | true 12 | 13 | 14 | 15 | BlazorWorker.WorkerCore.xml 16 | Exe 17 | 18 | 19 | 20 | 1701;1702;1591;CA1416 21 | 22 | 23 | 24 | 25 | $(MSBuildProjectName).xml 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/DOMObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BlazorWorker.WorkerCore.WebAssemblyBindingsProxy; 3 | 4 | 5 | namespace BlazorWorker.WorkerCore 6 | { 7 | 8 | // Serves as a wrapper around a JSObject. 9 | class DOMObject : IDisposable 10 | { 11 | public static DOMObject Self { get; } = new DOMObject("self"); 12 | 13 | public JSObject ManagedJSObject { get; private set; } 14 | 15 | public DOMObject(JSObject jsobject) 16 | { 17 | ManagedJSObject = jsobject ?? throw new ArgumentNullException(nameof(jsobject)); 18 | } 19 | 20 | public DOMObject(string globalName) : this(new JSObject(Runtime.GetGlobalObject(globalName))) 21 | { } 22 | 23 | public object Invoke(string method, params object[] args) 24 | { 25 | return ManagedJSObject.Invoke(method, args); 26 | } 27 | 28 | public void Dispose() 29 | { 30 | // Dispose of unmanaged resources. 31 | Dispose(true); 32 | // Suppress finalization. 33 | GC.SuppressFinalize(this); 34 | } 35 | 36 | // Protected implementation of Dispose pattern. 37 | protected virtual void Dispose(bool disposing) 38 | { 39 | 40 | if (disposing) 41 | { 42 | 43 | // Free any other managed objects here. 44 | // 45 | } 46 | 47 | // Free any unmanaged objects here. 48 | // 49 | ManagedJSObject?.Dispose(); 50 | ManagedJSObject = null; 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/FetchResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.Core 2 | { 3 | public class FetchResponse 4 | { 5 | public string Base64Data { get; set; } 6 | 7 | public string Url { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/IWorker.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace BlazorWorker.Core 6 | { 7 | 8 | public interface IWorker : IWorkerMessageService, IAsyncDisposable 9 | { 10 | bool IsInitialized { get; } 11 | 12 | long Identifier { get; } 13 | 14 | Task InitAsync(WorkerInitOptions initOptions); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/IWorkerMessageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.WorkerCore 5 | { 6 | public interface IWorkerMessageService 7 | { 8 | /// 9 | /// Events for incoming messages to the current context 10 | /// 11 | event EventHandler IncomingMessage; 12 | 13 | /// 14 | /// Post a message to the context this message service belongs to 15 | /// 16 | /// 17 | /// 18 | Task PostMessageAsync(string message); 19 | 20 | /// 21 | /// Post a message that can be read directly on the main js thread using the "blazorworker:jsdirect" event on the window object 22 | /// 23 | /// 24 | /// 25 | Task PostMessageJsDirectAsync(string message); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/InstanceWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerCore 4 | { 5 | public class InstanceWrapper : IDisposable 6 | { 7 | public object Instance { get; set; } 8 | public IDisposable Services { get; set; } 9 | 10 | public void Dispose() 11 | { 12 | if (Instance is IDisposable disposable) 13 | { 14 | disposable.Dispose(); 15 | } 16 | 17 | Services?.Dispose(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/LinkerConfig.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/MessageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | #if NET7_0_OR_GREATER 4 | using System.Runtime.InteropServices.JavaScript; 5 | #endif 6 | using System.Threading.Tasks; 7 | 8 | #if NET7_0_OR_GREATER 9 | namespace BlazorWorker.WorkerCore 10 | { 11 | /// 12 | /// Simple static message service that runs in the worker thread. 13 | /// 14 | public partial class MessageService 15 | { 16 | public static event EventHandler Message; 17 | 18 | [JSExport] 19 | public static void OnMessage(string message) 20 | { 21 | Message?.Invoke(null, message); 22 | #if DEBUG 23 | Console.WriteLine($"{nameof(MessageService)}.{nameof(OnMessage)}: {message}"); 24 | #endif 25 | } 26 | 27 | [JSImport("PostMessage", "BlazorWorker.js")] 28 | public static partial void PostMessage(string message); 29 | 30 | [JSImport("PostMessageJsDirect", "BlazorWorker.js")] 31 | public static partial void PostMessageJsDirect(string message); 32 | } 33 | } 34 | #else 35 | 36 | namespace BlazorWorker.WorkerCore 37 | { 38 | /// 39 | /// Simple static message service that runs in the worker thread. 40 | /// 41 | public class MessageService 42 | { 43 | private static readonly DOMObject self = DOMObject.Self; 44 | 45 | public static event EventHandler Message; 46 | 47 | static MessageService() 48 | { 49 | } 50 | 51 | public static void OnMessage(string message) 52 | { 53 | Message?.Invoke(null, message); 54 | #if DEBUG 55 | Console.WriteLine($"{nameof(MessageService)}.{nameof(OnMessage)}: {message}"); 56 | #endif 57 | } 58 | public static void PostMessage(string message) 59 | { 60 | self.Invoke("postMessage", message); 61 | } 62 | 63 | } 64 | } 65 | #endif -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NET7_0_OR_GREATER 3 | 4 | Console.WriteLine("BlazorWorker.WorkerCore:Started"); 5 | 6 | #else 7 | 8 | namespace BlazorWorker.WorkerCore 9 | { 10 | public class Program 11 | { 12 | static void Main() 13 | { 14 | Console.WriteLine("Hello, Dotnet Worker in Browser!"); 15 | } 16 | } 17 | } 18 | #endif -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/CSVSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 7 | { 8 | public class CSVSerializer 9 | { 10 | public const char EscapeChar = '\\'; 11 | public const char Separator = '|'; 12 | 13 | public static string EscapeString(string s) 14 | { 15 | return s?.Replace(EscapeChar, EscapeChar) 16 | .Replace(Separator.ToString(), new string(new[] { EscapeChar, Separator })); 17 | } 18 | 19 | public static string Serialize(string prefix, params object[] fields) 20 | { 21 | return string.Join(Separator.ToString(), new[] { prefix }.Concat(fields)); 22 | } 23 | 24 | public static void Deserialize(string prefix, string message, Queue> fieldParserQueue) 25 | { 26 | if (!message.StartsWith(prefix)) 27 | { 28 | throw new FormatException($"Unexpected start of message, expected {prefix}"); 29 | } 30 | var body = message.Substring(prefix.Length+1); 31 | var sb = new StringBuilder(body.Length); 32 | var lastChar = ' '; 33 | var pos = -1; 34 | 35 | void nextParser() { 36 | var fieldValue = sb.ToString(); 37 | try 38 | { 39 | fieldParserQueue.Dequeue()(fieldValue); 40 | } 41 | catch (Exception e) 42 | { 43 | throw new FormatException($"Error when parsing field value '{fieldValue}' message prefixed {prefix}. body '{body}' buffer left '{body.Substring(pos)}", e); 44 | } 45 | } 46 | 47 | foreach (var chr in body) 48 | { 49 | pos++; 50 | if (lastChar == EscapeChar && chr == EscapeChar) 51 | { 52 | continue; 53 | } 54 | else if (lastChar != EscapeChar && chr == Separator) 55 | { 56 | nextParser(); 57 | 58 | if (fieldParserQueue.Count == 0) 59 | { 60 | return; 61 | } 62 | 63 | sb.Clear(); 64 | } 65 | else 66 | { 67 | sb.Append(chr); 68 | lastChar = chr; 69 | } 70 | 71 | } 72 | 73 | if(fieldParserQueue.Count > 1) 74 | { 75 | throw new FormatException($"Unexpected end of message prefixed {prefix}"); 76 | } 77 | 78 | nextParser(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/DisposeInstanceRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 6 | { 7 | public class DisposeInstanceRequest 8 | { 9 | public readonly static string Prefix = 10 | $"{SimpleInstanceService.MessagePrefix}{SimpleInstanceService.DiposeMessagePrefix}"; 11 | 12 | public long CallId { get; set; } 13 | 14 | public long InstanceId { get; set; } 15 | 16 | public static bool CanDeserialize(string message) 17 | { 18 | return message.StartsWith(Prefix); 19 | } 20 | 21 | public static DisposeInstanceRequest Deserialize(string message) 22 | { 23 | var result = new DisposeInstanceRequest(); 24 | var parsers = new Queue>( 25 | new Action[] 26 | { 27 | s => result.CallId = long.Parse(s), 28 | s => result.InstanceId = long.Parse(s) 29 | }); 30 | 31 | CSVSerializer.Deserialize(Prefix, message, parsers); 32 | 33 | return result; 34 | } 35 | 36 | public string Serialize() 37 | { 38 | return CSVSerializer.Serialize(Prefix, CallId, InstanceId); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/DisposeResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 5 | { 6 | public class DisposeResult 7 | { 8 | public static readonly string Prefix = $"{SimpleInstanceService.MessagePrefix}{SimpleInstanceService.DiposeResultMessagePrefix}"; 9 | 10 | public long CallId { get; set; } 11 | 12 | public bool IsSuccess { get; set; } 13 | public long InstanceId { get; set; } 14 | 15 | public string ExceptionMessage { get; set; } = string.Empty; 16 | 17 | public string FullExceptionString { get; set; } = string.Empty; 18 | 19 | public Exception Exception { get; internal set; } 20 | 21 | internal string Serialize() 22 | { 23 | return CSVSerializer.Serialize(Prefix, 24 | this.CallId, 25 | (this.IsSuccess ? 1 : 0), 26 | CSVSerializer.EscapeString(this.ExceptionMessage), 27 | CSVSerializer.EscapeString(this.FullExceptionString)); 28 | } 29 | 30 | public static bool CanDeserialize(string message) 31 | { 32 | return message.StartsWith(Prefix); 33 | } 34 | 35 | public static DisposeResult Deserialize(string message) 36 | { 37 | var result = new DisposeResult(); 38 | 39 | var parsers = new Queue>( 40 | new Action[] { 41 | s => result.CallId = long.Parse(s), 42 | s => result.IsSuccess = s == "1", 43 | s => result.ExceptionMessage = s, 44 | s => result.FullExceptionString = s 45 | }); 46 | 47 | CSVSerializer.Deserialize(Prefix, message, parsers); 48 | return result; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/ISimpleInstanceService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 4 | { 5 | public interface ISimpleInstanceService 6 | { 7 | Task DisposeInstance(DisposeInstanceRequest request); 8 | Task InitInstance(InitInstanceRequest initInstanceRequest); 9 | } 10 | } -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/InitInstanceRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 6 | { 7 | public class InitInstanceRequest 8 | { 9 | 10 | public static readonly string Prefix = $"{SimpleInstanceService.MessagePrefix}{SimpleInstanceService.InitInstanceMessagePrefix}"; 11 | public long CallId { get; set; } 12 | public long Id { get; set; } 13 | public string TypeName { get; set; } 14 | public string AssemblyName { get; set; } 15 | 16 | internal static bool CanDeserialize(string initMessage) 17 | { 18 | return initMessage.StartsWith(Prefix); 19 | } 20 | 21 | internal static InitInstanceRequest Deserialize(string initMessage) 22 | { 23 | var result = new InitInstanceRequest(); 24 | 25 | var parsers = new Queue>( 26 | new Action[] { 27 | s => result.CallId = long.Parse(s), 28 | s => result.Id = long.Parse(s), 29 | s => result.TypeName = s, 30 | s => result.AssemblyName = s 31 | }); 32 | 33 | CSVSerializer.Deserialize(Prefix, initMessage, parsers); 34 | return result; 35 | } 36 | 37 | public string Serialize() 38 | { 39 | return CSVSerializer.Serialize(Prefix, 40 | CallId, 41 | Id, 42 | CSVSerializer.EscapeString(TypeName), 43 | CSVSerializer.EscapeString(AssemblyName)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/InitInstanceResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 5 | { 6 | public class InitInstanceResult 7 | { 8 | public static readonly string Prefix = $"{SimpleInstanceService.MessagePrefix}{SimpleInstanceService.InitInstanceResultMessagePrefix}"; 9 | 10 | public long CallId { get; set; } 11 | public bool IsSuccess { get; set; } 12 | public long InstanceId { get; set; } 13 | 14 | public object Instance { get; set; } 15 | 16 | public string ExceptionMessage { get; set; } = string.Empty; 17 | 18 | public string FullExceptionString { get; set; } = string.Empty; 19 | 20 | public Exception Exception { get; internal set; } 21 | 22 | internal string Serialize() 23 | { 24 | return CSVSerializer.Serialize(Prefix, 25 | this.CallId, 26 | this.IsSuccess? 1 : 0, 27 | CSVSerializer.EscapeString(this.ExceptionMessage), 28 | CSVSerializer.EscapeString(this.FullExceptionString)); 29 | 30 | } 31 | public static bool CanDeserialize(string message) 32 | { 33 | return message.StartsWith(Prefix); 34 | } 35 | 36 | public static InitInstanceResult Deserialize(string message) 37 | { 38 | var result = new InitInstanceResult(); 39 | 40 | var parsers = new Queue>( 41 | new Action[] { 42 | s => result.CallId = long.Parse(s), 43 | s => result.IsSuccess = s == "1", 44 | s => result.ExceptionMessage = s, 45 | s => result.FullExceptionString = s 46 | }); 47 | 48 | CSVSerializer.Deserialize(Prefix, message, parsers); 49 | return result; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/InitServiceResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 6 | { 7 | public class InitServiceResult 8 | { 9 | public static readonly string Prefix = $"{SimpleInstanceService.MessagePrefix}{SimpleInstanceService.InitServiceResultMessagePrefix}"; 10 | 11 | public static bool CanDeserialize(string message) 12 | { 13 | return message.StartsWith(Prefix); 14 | } 15 | 16 | public static InitServiceResult Deserialize(string message) 17 | { 18 | return new InitServiceResult(); 19 | } 20 | 21 | public string Serialize() 22 | { 23 | return Prefix; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/SimpleInstanceService/InjectableMessageService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.WorkerCore.SimpleInstanceService 5 | { 6 | public delegate bool IsInfrastructureMessage(string message); 7 | public class InjectableMessageService : IWorkerMessageService, IDisposable 8 | { 9 | private readonly IsInfrastructureMessage isInfrastructureMessage; 10 | 11 | public InjectableMessageService(IsInfrastructureMessage isInfrastructureMessage) 12 | { 13 | MessageService.Message += OnIncomingMessage; 14 | this.isInfrastructureMessage = isInfrastructureMessage; 15 | } 16 | 17 | private void OnIncomingMessage(object sender, string rawMessage) 18 | { 19 | if (isInfrastructureMessage(rawMessage)) 20 | { 21 | // Prevents Infrastructure messages from propagating downwards 22 | return; 23 | } 24 | 25 | IncomingMessage?.Invoke(sender, rawMessage); 26 | } 27 | 28 | public event EventHandler IncomingMessage; 29 | 30 | public void Dispose() 31 | { 32 | MessageService.Message -= OnIncomingMessage; 33 | } 34 | 35 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 36 | public async Task PostMessageAsync(string message) 37 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 38 | { 39 | #if DEBUG 40 | Console.WriteLine($"{nameof(InjectableMessageService)}.{nameof(PostMessageAsync)}('{message}')"); 41 | #endif 42 | MessageService.PostMessage(message); 43 | } 44 | 45 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 46 | public async Task PostMessageJsDirectAsync(string message) 47 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 48 | { 49 | MessageService.PostMessageJsDirect(message); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/TaskRegister.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorWorker.WorkerCore 8 | { 9 | public class TaskRegister : ConcurrentDictionary> 10 | { 11 | public (long, TaskCompletionSource) CreateAndAdd() 12 | { 13 | var tcs = new TaskCompletionSource(); 14 | var id = Interlocked.Increment(ref TaskRegister.idSource); 15 | var retries = 100; 16 | string errorMessage = string.Empty; 17 | while (!this.TryAdd(id, tcs)) 18 | { 19 | if (retries < 0) 20 | { 21 | throw new InvalidOperationException(errorMessage); 22 | } 23 | 24 | errorMessage = $"{nameof(TaskRegister)}: Unable to add task id {id} as it already exists. This may happen if a task has not been properly disposed, awaited, or is running in an infinite loop."; 25 | id = Interlocked.Increment(ref TaskRegister.idSource); 26 | retries--; 27 | } 28 | 29 | if (retries < 100) { 30 | Console.Error.WriteLine(errorMessage); 31 | } 32 | 33 | return (id, tcs); 34 | } 35 | } 36 | 37 | public class TaskRegister : TaskRegister { 38 | public static long idSource; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/WebAssemblyBindingsProxy/JSObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.WorkerCore.WebAssemblyBindingsProxy 4 | { 5 | internal class JSObject : IDisposable 6 | { 7 | public delegate object InvokeDelegate(string method, params object[] parameters); 8 | public delegate void DisposeDelegate(); 9 | private readonly InvokeDelegate _invokeMethodDelegate; 10 | private readonly DisposeDelegate _disposeMethodDelegate; 11 | 12 | public JSObject(object target) 13 | { 14 | var type = target.GetType(); 15 | var invokeMethod = type.GetMethod(nameof(Invoke)); 16 | var disposeMethod = type.GetMethod(nameof(Dispose)); 17 | _invokeMethodDelegate = Delegate.CreateDelegate(typeof(InvokeDelegate), target, invokeMethod) as InvokeDelegate; 18 | _disposeMethodDelegate = Delegate.CreateDelegate(typeof(DisposeDelegate), target, disposeMethod) as DisposeDelegate; 19 | } 20 | 21 | public object Invoke(string method, params object[] parameters) => _invokeMethodDelegate(method, parameters); 22 | 23 | public void Dispose() => _disposeMethodDelegate(); 24 | } 25 | } -------------------------------------------------------------------------------- /src/BlazorWorker.WorkerCore/WebAssemblyBindingsProxy/Runtime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace BlazorWorker.WorkerCore.WebAssemblyBindingsProxy 5 | { 6 | internal class Runtime 7 | { 8 | #if NETSTANDARD21 9 | private const string assembly = "WebAssembly.Bindings"; 10 | private static readonly string type = $"WebAssembly.{nameof(Runtime)}"; 11 | #endif 12 | 13 | #if NET5_0_OR_GREATER 14 | private const string assembly = "System.Private.Runtime.InteropServices.JavaScript"; 15 | private static readonly string type = $"System.Runtime.InteropServices.JavaScript.{nameof(Runtime)}"; 16 | #endif 17 | private delegate object GetGlobalObjectDelegate(string globalObjectName); 18 | 19 | private static Assembly SourceAssembly => Assembly.Load(assembly) 20 | ?? throw new InvalidOperationException($"Unable to load assembly {assembly}"); 21 | 22 | private static GetGlobalObjectDelegate _getGlobalObjectMethod = 23 | SourceAssembly 24 | .GetType(type)? 25 | .GetMethod(nameof(GetGlobalObject))? 26 | .CreateDelegate(typeof(GetGlobalObjectDelegate)) as GetGlobalObjectDelegate; 27 | 28 | public static object GetGlobalObject(string globalObjectName) => _getGlobalObjectMethod?.Invoke(globalObjectName) 29 | ?? throw new InvalidOperationException($"Unable to load method {type}.{nameof(GetGlobalObject)} from assembly {assembly}"); 30 | } 31 | } -------------------------------------------------------------------------------- /src/BlazorWorker/BlazorWorker.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0;net8.0 5 | Tor Knutsson (Tewr) 6 | BlazorWorker 7 | MIT 8 | Use dotnet Web Workers Threads in Blazor 9 | https://github.com/Tewr/BlazorWorker 10 | https://github.com/Tewr/BlazorWorker 11 | WebWorker Worker Process Threading Multithreading Blazor Isolation 12 | Tewr.BlazorWorker.Core 13 | Debug;Release;Nuget 14 | 4.2.0 15 | 4.2.0.0 16 | BlazorWorker.Core.xml 17 | icon.png 18 | false 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | all 43 | 44 | 45 | 46 | 47 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage 48 | true 49 | 50 | 51 | 52 | 1701;1702;1591;CA1416 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/BlazorWorker/BlazorWorker.Core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BlazorWorker.Core 5 | 6 | 7 | 8 | 9 | Adds as a singleton service 10 | to the specified . 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/BlazorWorker/CoreInstanceService/CoreInstanceHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.Core.CoreInstanceService 5 | { 6 | internal class CoreInstanceHandle : IInstanceHandle 7 | { 8 | private Func onDispose; 9 | 10 | public CoreInstanceHandle(Func onDispose) 11 | { 12 | this.onDispose = onDispose; 13 | } 14 | 15 | public async ValueTask DisposeAsync() 16 | { 17 | await this.onDispose(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/BlazorWorker/CoreInstanceService/ICoreInstanceService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.Core.CoreInstanceService 5 | { 6 | public interface ICoreInstanceService 7 | { 8 | Task CreateInstance(Type type); 9 | Task CreateInstance(); 10 | 11 | Task CreateInstance(Action options); 12 | 13 | Task CreateInstance(WorkerInitOptions options); 14 | } 15 | 16 | public interface IInstanceHandle : IAsyncDisposable { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker/CoreInstanceService/SimpleInstanceServiceExtension.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Core.SimpleInstanceService; 2 | using System; 3 | 4 | namespace BlazorWorker.Core.CoreInstanceService 5 | { 6 | public static class SimpleInstanceServiceExtension 7 | { 8 | public static ICoreInstanceService CreateCoreInstanceService(this IWorker source) 9 | { 10 | return new CoreInstanceService(new SimpleInstanceServiceProxy(source)); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/BlazorWorker/CoreInstanceService/WorkerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorWorker.Core.CoreInstanceService 4 | { 5 | public abstract class WorkerException : Exception 6 | { 7 | public WorkerException(string message, string fullMessage) : base(message) 8 | { 9 | FullMessage = fullMessage; 10 | } 11 | 12 | public string FullMessage { get; } 13 | 14 | public override string ToString() 15 | { 16 | return $"{base.ToString()}{Environment.NewLine} --> Worker full exception: {FullMessage}"; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/BlazorWorker/CoreInstanceService/WorkerInstanceDisposeException.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.Core.CoreInstanceService 2 | { 3 | public class WorkerInstanceDisposeException : WorkerException 4 | { 5 | public WorkerInstanceDisposeException(string message, string fullMessage) 6 | :base($"Error when disposing instance: {message}", fullMessage) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorWorker/CoreInstanceService/WorkerInstanceInitializeException.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.Core.CoreInstanceService 2 | { 3 | public class WorkerInstanceInitializeException : WorkerException 4 | { 5 | public WorkerInstanceInitializeException(string message, string fullMessage) 6 | :base($"Error when initializing instance: {message}", fullMessage) 7 | { 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/BlazorWorker/DependencyHintAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace BlazorWorker.Core 5 | { 6 | internal class DependencyHintAttribute : Attribute 7 | { 8 | public DependencyHintAttribute(Type dependsOn, params Type[] dependsOnList) 9 | { 10 | DependsOn = new[] { dependsOn }.Concat(dependsOnList).ToArray(); 11 | } 12 | 13 | public Type[] DependsOn { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/BlazorWorker/IWorkerFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BlazorWorker.Core 4 | { 5 | public interface IWorkerFactory 6 | { 7 | Task CreateAsync(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/BlazorWorker/InstanceHandle.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using System; 3 | 4 | namespace BlazorWorker.Core 5 | { 6 | public class InstanceHandle : IDisposable 7 | { 8 | public InstanceHandle( 9 | IWorkerMessageService messageService, 10 | Type serviceType, 11 | long identifier, 12 | Action onDispose) 13 | { 14 | MessageService = messageService; 15 | ServiceType = serviceType; 16 | Identifier = identifier; 17 | OnDispose = onDispose; 18 | } 19 | 20 | public IWorkerMessageService MessageService { get; } 21 | 22 | public Type ServiceType { get; } 23 | public long Identifier { get; } 24 | public Action OnDispose { get; } 25 | 26 | public void Dispose() 27 | { 28 | OnDispose?.Invoke(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/BlazorWorker/MonoTypeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | 6 | namespace BlazorWorker.Core 7 | { 8 | public static class MonoTypeHelper 9 | { 10 | public static MethodIdentifier GetStaticMethodId(string method) 11 | { 12 | var owningType = typeof(T); 13 | if (!owningType.GetRuntimeMethods().Any(x => x.IsStatic && x.Name == method)) 14 | { 15 | throw new ArgumentException($"Method '{method}' is not a static member of type {owningType.Name}", nameof(method)); 16 | } 17 | return new MethodIdentifier 18 | { 19 | AssemblyName = owningType.Assembly.GetName().Name, 20 | FullMethodName = $"{owningType.FullName}.{method}" 21 | }; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlazorWorker/ScriptLoader.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorWorker.Core 10 | { 11 | public class ScriptLoader 12 | { 13 | private static readonly IReadOnlyDictionary escapeScriptTextReplacements = 14 | new Dictionary { { @"\", @"\\" }, { "\r", @"\r" }, { "\n", @"\n" }, { "'", @"\'" }, { "\"", @"\""" } }; 15 | 16 | private readonly IJSRuntime jsRuntime; 17 | 18 | public ScriptLoader(IJSRuntime jSRuntime) 19 | { 20 | this.jsRuntime = jSRuntime; 21 | } 22 | 23 | public async Task InitScript() 24 | { 25 | if (await IsLoaded()) 26 | { 27 | return; 28 | } 29 | 30 | string scriptContent; 31 | var resourceName = 32 | "BlazorWorker.Core.BlazorWorker.js"; 33 | 34 | var stream = this.GetType().Assembly.GetManifestResourceStream(resourceName); 35 | using (stream) 36 | { 37 | using (var streamReader = new StreamReader(stream)) 38 | { 39 | scriptContent = await streamReader.ReadToEndAsync(); 40 | } 41 | } 42 | 43 | await ExecuteRawScriptAsync(scriptContent); 44 | var loaderLoopBreaker = 0; 45 | while (!await IsLoaded()) 46 | { 47 | loaderLoopBreaker++; 48 | await Task.Delay(100); 49 | 50 | // Fail after 3s not to block and hide any other possible error 51 | if (loaderLoopBreaker > 25) 52 | { 53 | throw new InvalidOperationException("Unable to initialize BlazorWorker.js"); 54 | } 55 | } 56 | } 57 | private async Task IsLoaded() 58 | { 59 | return await jsRuntime.InvokeAsync("window.hasOwnProperty", "BlazorWorker"); 60 | } 61 | private async Task ExecuteRawScriptAsync(string scriptContent) 62 | { 63 | scriptContent = escapeScriptTextReplacements.Aggregate(scriptContent, (r, pair) => r.Replace(pair.Key, pair.Value)); 64 | var blob = $"URL.createObjectURL(new Blob([\"{scriptContent}\"],{{ \"type\": \"text/javascript\"}}))"; 65 | var bootStrapScript = $"(function(){{var d = document; var s = d.createElement('script'); s.async=false; s.src={blob}; d.head.appendChild(s); d.head.removeChild(s);}})();"; 66 | await jsRuntime.InvokeVoidAsync("eval", bootStrapScript); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/BlazorWorker/SetupExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace BlazorWorker.Core 4 | { 5 | public static class SetupExtensions 6 | { 7 | /// 8 | /// Adds as a singleton service 9 | /// to the specified . 10 | /// 11 | /// 12 | public static IServiceCollection AddWorkerFactory(this IServiceCollection services) 13 | { 14 | services.AddSingleton(); 15 | return services; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/BlazorWorker/SimpleInstanceService/SimpleInstanceServiceProxy.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using BlazorWorker.WorkerCore.SimpleInstanceService; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorWorker.Core.SimpleInstanceService 8 | { 9 | public class SimpleInstanceServiceProxy : ISimpleInstanceService 10 | { 11 | private readonly IWorker worker; 12 | private readonly TaskRegister disposeResultRegister = new(); 13 | private readonly TaskRegister initInstanceRegister = new(); 14 | private TaskCompletionSource initWorker; 15 | 16 | public bool IsInitialized { get; internal set; } 17 | 18 | public SimpleInstanceServiceProxy(IWorker worker) 19 | { 20 | this.worker = worker; 21 | this.worker.IncomingMessage += OnIncomingMessage; 22 | } 23 | 24 | public async Task InitializeAsync(WorkerInitOptions options = null) 25 | { 26 | if (!IsInitialized) 27 | { 28 | if (!this.worker.IsInitialized) 29 | { 30 | initWorker = new TaskCompletionSource(); 31 | await this.worker.InitAsync(options); 32 | if (this.worker is WorkerProxy proxy) 33 | { 34 | proxy.IsInitialized = true; 35 | } 36 | await this.initWorker.Task; 37 | } 38 | 39 | IsInitialized = true; 40 | } 41 | } 42 | 43 | private void OnIncomingMessage(object sender, string message) 44 | { 45 | #if DEBUG 46 | Console.WriteLine($"{nameof(SimpleInstanceServiceProxy)}:{message}"); 47 | #endif 48 | if (DisposeResult.CanDeserialize(message)) { 49 | var result = DisposeResult.Deserialize(message); 50 | if (disposeResultRegister.TryRemove(result.CallId, out var taskCompletionSource)) 51 | { 52 | taskCompletionSource.SetResult(result); 53 | } 54 | return; 55 | } 56 | 57 | if (InitServiceResult.CanDeserialize(message)) 58 | { 59 | initWorker.SetResult(InitServiceResult.Deserialize(message)); 60 | return; 61 | } 62 | 63 | if (InitInstanceResult.CanDeserialize(message)) 64 | { 65 | var result = InitInstanceResult.Deserialize(message); 66 | if (initInstanceRegister.TryRemove(result.CallId, out var taskCompletionSource)) 67 | { 68 | taskCompletionSource.SetResult(result); 69 | } 70 | return; 71 | } 72 | } 73 | 74 | public async Task DisposeInstance(DisposeInstanceRequest request) 75 | { 76 | var (callIdSource, tcs) = disposeResultRegister.CreateAndAdd(); 77 | request.CallId = callIdSource; 78 | await this.worker.PostMessageAsync(request.Serialize()); 79 | return await tcs.Task; 80 | } 81 | 82 | public async Task InitInstance(InitInstanceRequest request) 83 | { 84 | var (callIdSource, tcs) = initInstanceRegister.CreateAndAdd(); 85 | request.CallId = callIdSource; 86 | await this.worker.PostMessageAsync(request.Serialize()); 87 | return await tcs.Task; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/BlazorWorker/WorkerFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorWorker.Core 5 | { 6 | public class WorkerFactory : IWorkerFactory 7 | { 8 | private readonly IJSRuntime jsRuntime; 9 | 10 | public WorkerFactory(IJSRuntime jsRuntime) 11 | { 12 | this.jsRuntime = jsRuntime; 13 | } 14 | 15 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 16 | public async Task CreateAsync()//WorkerInitOptions initOptions) 17 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 18 | { 19 | var worker = new WorkerProxy(jsRuntime); 20 | //await worker.InitAsync(initOptions); 21 | return worker; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlazorWorker/WorkerProxy.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.WorkerCore; 2 | using Microsoft.JSInterop; 3 | using System; 4 | using System.Threading.Tasks; 5 | namespace BlazorWorker.Core 6 | { 7 | [DependencyHint(typeof(MessageService))] 8 | public class WorkerProxy : IWorker 9 | { 10 | private readonly IJSRuntime jsRuntime; 11 | private readonly ScriptLoader scriptLoader; 12 | private static long idSource; 13 | private bool isDisposed = false; 14 | private static readonly MethodIdentifier messageMethod; 15 | private readonly DotNetObjectReference thisReference; 16 | 17 | 18 | public event EventHandler IncomingMessage; 19 | public bool IsInitialized { get; set; } 20 | static WorkerProxy() 21 | { 22 | var messageServiceType = typeof(MessageService); 23 | messageMethod = MonoTypeHelper.GetStaticMethodId(nameof(MessageService.OnMessage)); 24 | //$"[{messageServiceType.Assembly.GetName().Name}]{messageServiceType.FullName}:{nameof(MessageService.OnMessage)}"; 25 | } 26 | 27 | public WorkerProxy(IJSRuntime jsRuntime) 28 | { 29 | this.jsRuntime = jsRuntime; 30 | this.scriptLoader = new ScriptLoader(this.jsRuntime); 31 | this.Identifier = ++idSource; 32 | thisReference = DotNetObjectReference.Create(this); 33 | } 34 | 35 | public async ValueTask DisposeAsync() 36 | { 37 | if (!isDisposed) 38 | { 39 | await this.jsRuntime.InvokeVoidAsync("BlazorWorker.disposeWorker", this.Identifier); 40 | thisReference.Dispose(); 41 | isDisposed = true; 42 | } 43 | } 44 | 45 | public async Task InitAsync(WorkerInitOptions initOptions) 46 | { 47 | await this.scriptLoader.InitScript(); 48 | 49 | await this.jsRuntime.InvokeVoidAsync( 50 | "BlazorWorker.initWorker", 51 | this.Identifier, 52 | thisReference, 53 | new WorkerInitOptions { 54 | 55 | DependentAssemblyFilenames = 56 | WorkerProxyDependencies.DependentAssemblyFilenames, 57 | CallbackMethod = nameof(OnMessage), 58 | MessageEndPoint = messageMethod 59 | }.MergeWith(initOptions)); 60 | } 61 | 62 | [JSInvokable] 63 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 64 | public async Task OnMessage(string message) 65 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 66 | { 67 | IncomingMessage?.Invoke(this, message); 68 | } 69 | 70 | public async Task PostMessageAsync(string message) 71 | { 72 | await jsRuntime.InvokeVoidAsync("BlazorWorker.postMessage", this.Identifier, message); 73 | } 74 | 75 | public async Task PostMessageJsDirectAsync(string message) 76 | { 77 | throw new NotSupportedException("JsDirect calls are only supported in the direction from worker to main js"); 78 | } 79 | 80 | 81 | 82 | public long Identifier { get; } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/BlazorWorker/WorkerProxyDependencies.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorWorker.Core 2 | { 3 | public class WorkerProxyDependencies 4 | { 5 | #if NETSTANDARD21 6 | public static readonly string[] DependentAssemblyFilenames = new[] 7 | { 8 | "BlazorWorker.WorkerCore.dll", 9 | "netstandard.dll", 10 | "mscorlib.dll", 11 | "WebAssembly.Bindings.dll", 12 | "System.dll", 13 | "System.Core.dll" 14 | }; 15 | #endif 16 | 17 | #if NET5_0_OR_GREATER 18 | public static readonly string[] DependentAssemblyFilenames = new[] 19 | { 20 | "BlazorWorker.WorkerCore.dll", 21 | "netstandard.dll", 22 | "mscorlib.dll", 23 | "System.dll", 24 | "System.Core.dll", 25 | "System.Buffers.dll", 26 | "System.Collections.dll", 27 | "System.Configuration.dll", 28 | "System.Console.dll", 29 | "System.Core.dll", 30 | "System.Diagnostics.Debug.dll", 31 | "System.Diagnostics.DiagnosticSource.dll", 32 | "System.Diagnostics.StackTrace.dll", 33 | "System.Diagnostics.TraceSource.dll", 34 | "System.Dynamic.Runtime.dll", 35 | "System.Globalization.Calendars.dll", 36 | "System.Globalization.Extensions.dll", 37 | "System.Globalization.dll", 38 | "System.Linq.Expressions.dll", 39 | "System.Linq.Queryable.dll", 40 | "System.Linq.dll", 41 | "System.Memory.dll", 42 | "System.Numerics.Vectors.dll", 43 | "System.Numerics.dll", 44 | "System.ObjectModel.dll", 45 | "System.Private.CoreLib.dll", 46 | "System.Private.Runtime.InteropServices.JavaScript.dll", 47 | "System.Private.Uri.dll", 48 | "System.Private.Xml.Linq.dll", 49 | "System.Private.Xml.dll", 50 | "System.Reflection.DispatchProxy.dll", 51 | "System.Reflection.Extensions.dll", 52 | "System.Reflection.Metadata.dll", 53 | "System.Reflection.Primitives.dll", 54 | "System.Reflection.TypeExtensions.dll", 55 | "System.Reflection.dll", 56 | "System.Runtime.Extensions.dll", 57 | "System.Runtime.Handles.dll", 58 | "System.Runtime.InteropServices.RuntimeInformation.dll", 59 | "System.Runtime.InteropServices.dll", 60 | "System.Runtime.Intrinsics.dll", 61 | "System.Runtime.Loader.dll", 62 | "System.Runtime.Numerics.dll", 63 | "System.Runtime.dll", 64 | "System.Threading.Tasks.dll", 65 | "System.Threading.Thread.dll", 66 | "System.Threading.dll", 67 | }; 68 | #endif 69 | 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/BlazorWorker/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tewr/BlazorWorker/d5b86005df4d1a964beaed3ae3d03936221ee41b/src/BlazorWorker/icon.png -------------------------------------------------------------------------------- /src/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/BlazorWorker.Extensions.JSRuntimeTests/BlazorWorker.Extensions.JSRuntimeTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/BlazorWorker.Extensions.JSRuntimeTests/GenericNonPublicDelegateCacheTest.cs: -------------------------------------------------------------------------------- 1 | using BlazorWorker.Extensions.JSRuntime; 2 | using NUnit.Framework; 3 | using System.Collections.Generic; 4 | using static BlazorWorker.Extensions.JSRuntime.DotNetObjectReferenceTracker; 5 | 6 | namespace BlazorWorker.Extensions.JSRuntimeTests 7 | { 8 | public class GenericNonPublicDelegateCacheTests 9 | { 10 | [SetUp] 11 | public void Setup() 12 | { 13 | } 14 | 15 | [Test] 16 | public void GenericNonPublicDelegateCache_Returns_NonPublic_Delegates() 17 | { 18 | var genericDelegateCache = new GenericNonPublicDelegateCache(typeof(TypeWithPrivateThings)); 19 | var instance = new TypeWithPrivateThings(); 20 | var delegateTest = genericDelegateCache.GetDelegate, long, List>(instance, "DoThing"); 21 | 22 | var result = delegateTest.Invoke(new List() { { 1L }, { 2L }, { 3L } }); 23 | Assert.AreEqual(3+100, result); 24 | } 25 | 26 | public delegate long DoThingDelegate(List arg1); 27 | 28 | public class TypeWithPrivateThings { 29 | #pragma warning disable IDE0051 // Remove unused private members - accessed by reflection 30 | private long DoThing(List arg1) 31 | #pragma warning restore IDE0051 // Remove unused private members 32 | { 33 | return arg1.Count + 100; 34 | } 35 | } 36 | } 37 | } --------------------------------------------------------------------------------