diff --git a/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/ComplexSerialization.razor b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/ComplexSerialization.razor
new file mode 100644
index 0000000..e937d86
--- /dev/null
+++ b/src/BlazorWorker.Demo/Net8/Client/BlazorWorker.Demo.Client/Pages/ComplexSerialization.razor
@@ -0,0 +1,2 @@
+@page "/ComplexSerialization"
+
\ No newline at end of file
diff --git a/src/BlazorWorker.Demo/SharedPages/Pages/ComplexSerialization.razor b/src/BlazorWorker.Demo/SharedPages/Pages/ComplexSerialization.razor
new file mode 100644
index 0000000..dac3d94
--- /dev/null
+++ b/src/BlazorWorker.Demo/SharedPages/Pages/ComplexSerialization.razor
@@ -0,0 +1,133 @@
+@inject IWorkerFactory workerFactory
+
+@using BlazorWorker.BackgroundServiceFactory
+@using BlazorWorker.WorkerBackgroundService
+@using BlazorWorker.Demo.Shared
+@using BlazorWorker.Core
+
+
+
.NET Worker Multithreading
+
+ Complex / Custom Serialization
+
+
+
+
+
+
Output:
+
+
+@output
+
+
+
+
+
+
+@code {
+
+ string output;
+
+ IWorker worker;
+ IWorkerBackgroundService backgroundService;
+
+ string RunDisabled => Running ? "disabled" : null;
+ bool Running = false;
+
+
+ public class ComplexService
+ {
+ public ComplexServiceResponse ComplexCall(ComplexServiceArg arg)
+ {
+ return new ComplexServiceResponse
+ {
+ OriginalArg = arg,
+ OnlyInResponse = "This is only in response."
+ };
+ }
+ }
+
+ public class ComplexServiceArg
+ {
+ public string ThisIsJustAString { get; set; }
+ public OhLookARecord ARecord { get; set; }
+ public Dictionary ADictionary { get; set; }
+ public ComplexServiceArg TypeRecursive { get; set; }
+ }
+
+ public class ComplexServiceResponse
+ {
+ public ComplexServiceArg OriginalArg { get; set; }
+ public string OnlyInResponse { get; set; }
+ }
+
+ public record OhLookARecord
+ {
+ public int Number { get; set; }
+ }
+
+ public async Task OnClick(EventArgs _)
+ {
+ Running = true;
+ await this.InvokeAsync(StateHasChanged);
+
+ output = "";
+ var rn = Environment.NewLine;
+ try
+ {
+ if (worker == null)
+ {
+ output += $"Starting worker...{rn}";
+ await this.InvokeAsync(StateHasChanged);
+ worker = await workerFactory.CreateAsync();
+ }
+ if (backgroundService == null)
+ {
+
+ output += $"Starting BackgroundService...{rn}";
+ await this.InvokeAsync(StateHasChanged);
+ /*
+ * have a look here. This is the essence of this example.
+ * */
+
+ backgroundService = await worker.CreateBackgroundServiceAsync(options
+ => options.UseCustomExpressionSerializer(typeof(CustomSerializeLinqExpressionJsonSerializer)));
+ }
+
+ var sw = System.Diagnostics.Stopwatch.StartNew();
+
+ var complexArgInstance = new ComplexServiceArg
+ {
+ ThisIsJustAString = "just a string",
+ ARecord = new OhLookARecord { Number = 5 },
+ ADictionary = new Dictionary
+ {
+ { "Test", "TestValue" }
+ },
+ TypeRecursive = new ComplexServiceArg
+ {
+ ThisIsJustAString = "SubString"
+ }
+ };
+
+ var result = await backgroundService.RunAsync(service => service.ComplexCall(complexArgInstance));
+ var elapsed = sw.ElapsedMilliseconds;
+ output += $"{rn}result: " + System.Text.Json.JsonSerializer.Serialize(result);
+ output += $"{rn}roundtrip to worker in {elapsed}ms";
+ }
+ catch (Exception e)
+ {
+ output += $"{rn}Error = {e}";
+ }
+ finally
+ {
+ Running = false;
+ output += $"{rn}Done.";
+ }
+ }
+
+ private string LogDate()
+ {
+ return DateTime.Now.ToString("HH:mm:ss:fff");
+ }
+}
diff --git a/src/BlazorWorker.Demo/SharedPages/Shared/CustomExpressionSerializer.cs b/src/BlazorWorker.Demo/SharedPages/Shared/CustomExpressionSerializer.cs
new file mode 100644
index 0000000..4cfbef2
--- /dev/null
+++ b/src/BlazorWorker.Demo/SharedPages/Shared/CustomExpressionSerializer.cs
@@ -0,0 +1,47 @@
+using BlazorWorker.WorkerBackgroundService;
+using Serialize.Linq.Serializers;
+using System;
+using System.Linq.Expressions;
+using static BlazorWorker.Demo.SharedPages.Pages.ComplexSerialization;
+
+namespace BlazorWorker.Demo.SharedPages.Shared
+{
+ ///
+ /// Example 1: Simple custom expression Serializer using
+ /// as base class, but explicitly adds complex types as known types.
+ ///
+ public class CustomSerializeLinqExpressionJsonSerializer : SerializeLinqExpressionJsonSerializerBase
+ {
+ public override Type[] GetKnownTypes() =>
+ [typeof(ComplexServiceArg), typeof(ComplexServiceResponse), typeof(OhLookARecord)];
+ }
+
+ ///
+ /// Fully custom Expression Serializer, which uses but you could use an alternative implementation.
+ ///
+ public class CustomExpressionSerializer : IExpressionSerializer
+ {
+ private readonly ExpressionSerializer serializer;
+
+ public CustomExpressionSerializer()
+ {
+ var specificSerializer = new JsonSerializer();
+ specificSerializer.AddKnownType(typeof(ComplexServiceArg));
+ specificSerializer.AddKnownType(typeof(ComplexServiceResponse));
+ specificSerializer.AddKnownType(typeof(OhLookARecord));
+
+ this.serializer = new ExpressionSerializer(specificSerializer);
+ }
+
+ public Expression Deserialize(string expressionString)
+ {
+ return serializer.DeserializeText(expressionString);
+ }
+
+ public string Serialize(Expression expression)
+ {
+ return serializer.SerializeText(expression);
+ }
+ }
+
+}
diff --git a/src/BlazorWorker.Demo/SharedPages/Shared/NavMenuLinksModel.cs b/src/BlazorWorker.Demo/SharedPages/Shared/NavMenuLinksModel.cs
index 7c6f4c1..942f138 100644
--- a/src/BlazorWorker.Demo/SharedPages/Shared/NavMenuLinksModel.cs
+++ b/src/BlazorWorker.Demo/SharedPages/Shared/NavMenuLinksModel.cs
@@ -1,4 +1,5 @@
-using Microsoft.AspNetCore.Components;
+using BlazorWorker.Demo.SharedPages.Pages;
+using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using System;
using System.Collections.Generic;
@@ -34,6 +35,10 @@ public class NavMenuLinksModel
{
new() { Icon = "document", Href="IndexedDB", Text = "IndexedDB" }
},
+ {
+ new() { Icon = "document", Href="ComplexSerialization", Text = "ComplexSerialization" }
+ },
+
{
new() { Icon = "fork", Href="https://github.com/tewr/BlazorWorker", Text = "To the source!" }
},
diff --git a/src/BlazorWorker.ServiceFactory/BlazorWorker.BackgroundServiceFactory.xml b/src/BlazorWorker.ServiceFactory/BlazorWorker.BackgroundServiceFactory.xml
index dc8ded8..3c99b4f 100644
--- a/src/BlazorWorker.ServiceFactory/BlazorWorker.BackgroundServiceFactory.xml
+++ b/src/BlazorWorker.ServiceFactory/BlazorWorker.BackgroundServiceFactory.xml
@@ -30,5 +30,14 @@
Attached objects, notably parent worker proxy which may have been created without consumer being directly able to dispose
+
+
+ Sets a custom ExpressionSerializer type. Must implement ..
+
+
+ A type that implements
+
+
+
diff --git a/src/BlazorWorker.ServiceFactory/WorkerBackgroundServiceExtensions.cs b/src/BlazorWorker.ServiceFactory/WorkerBackgroundServiceExtensions.cs
index 9d7c686..22951c6 100644
--- a/src/BlazorWorker.ServiceFactory/WorkerBackgroundServiceExtensions.cs
+++ b/src/BlazorWorker.ServiceFactory/WorkerBackgroundServiceExtensions.cs
@@ -26,13 +26,17 @@ public static async Task> CreateBackgroundServiceAsy
{
throw new ArgumentNullException(nameof(webWorkerProxy));
}
-
- var proxy = new WorkerBackgroundServiceProxy(webWorkerProxy, new WebWorkerOptions());
if (workerInitOptions == null)
{
workerInitOptions = new WorkerInitOptions();
}
+ var webWorkerOptions = new WebWorkerOptions();
+ webWorkerOptions.SetExpressionSerializerFromInitOptions(workerInitOptions);
+
+ var proxy = new WorkerBackgroundServiceProxy(webWorkerProxy, webWorkerOptions);
+
+
await proxy.InitAsync(workerInitOptions);
return proxy;
}
@@ -68,7 +72,17 @@ public static async Task> CreateBackgroundSer
{
workerInitOptionsModifier(workerInitOptions);
}
- var factoryProxy = new WorkerBackgroundServiceProxy(webWorkerProxy, new WebWorkerOptions());
+
+ if (workerInitOptions == null)
+ {
+ workerInitOptions = new WorkerInitOptions();
+ }
+
+ var webWorkerOptions = new WebWorkerOptions();
+ webWorkerOptions.SetExpressionSerializerFromInitOptions(workerInitOptions);
+
+
+ var factoryProxy = new WorkerBackgroundServiceProxy(webWorkerProxy, webWorkerOptions);
await factoryProxy.InitAsync(workerInitOptions);
var newProxy = await factoryProxy.InitFromFactoryAsync(factoryExpression);
@@ -76,6 +90,14 @@ public static async Task> CreateBackgroundSer
return newProxy;
}
+ private static void SetExpressionSerializerFromInitOptions(this WebWorkerOptions target, WorkerInitOptions workerInitOptions)
+ {
+ if (workerInitOptions.EnvMap?.TryGetValue(WebWorkerOptions.ExpressionSerializerTypeEnvKey, out var serializerType) == true)
+ {
+ target.ExpressionSerializerType = Type.GetType(serializerType);
+ }
+ }
+
///
/// Creates a new background service using the specified
///
diff --git a/src/BlazorWorker.ServiceFactory/WorkerInitExtension.cs b/src/BlazorWorker.ServiceFactory/WorkerInitExtension.cs
new file mode 100644
index 0000000..e0ce40a
--- /dev/null
+++ b/src/BlazorWorker.ServiceFactory/WorkerInitExtension.cs
@@ -0,0 +1,28 @@
+using BlazorWorker.Core;
+using BlazorWorker.WorkerBackgroundService;
+using System;
+
+namespace BlazorWorker.BackgroundServiceFactory
+{
+ public static class WorkerInitExtension
+ {
+
+ ///
+ /// Sets a custom ExpressionSerializer type. Must implement ..
+ ///
+ ///
+ /// A type that implements
+ ///
+ ///
+ public static WorkerInitOptions UseCustomExpressionSerializer(this WorkerInitOptions source, Type expressionSerializerType)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ source.SetEnv(WebWorkerOptions.ExpressionSerializerTypeEnvKey, expressionSerializerType.AssemblyQualifiedName);
+ return source;
+ }
+ }
+}
diff --git a/src/BlazorWorker.WorkerBackgroundService/BlazorWorker.WorkerBackgroundService.xml b/src/BlazorWorker.WorkerBackgroundService/BlazorWorker.WorkerBackgroundService.xml
index 0a10643..52dba3d 100644
--- a/src/BlazorWorker.WorkerBackgroundService/BlazorWorker.WorkerBackgroundService.xml
+++ b/src/BlazorWorker.WorkerBackgroundService/BlazorWorker.WorkerBackgroundService.xml
@@ -54,5 +54,17 @@
Unregisters the event corresponding to the specified
+
+
+ Name of environment variable to be used for transferring the serializer typename
+
+
+
+
+ Ensures that the provided type implements
+
+
+
+
diff --git a/src/BlazorWorker.WorkerBackgroundService/SerializeLinqExpressionJsonSerializerBase.cs b/src/BlazorWorker.WorkerBackgroundService/SerializeLinqExpressionJsonSerializerBase.cs
new file mode 100644
index 0000000..0131970
--- /dev/null
+++ b/src/BlazorWorker.WorkerBackgroundService/SerializeLinqExpressionJsonSerializerBase.cs
@@ -0,0 +1,57 @@
+using Serialize.Linq.Serializers;
+using System;
+using System.Linq.Expressions;
+
+namespace BlazorWorker.WorkerBackgroundService
+{
+ ///
+ /// Base class for adding known types to a serializer using .
+ ///
+ public abstract class SerializeLinqExpressionJsonSerializerBase : IExpressionSerializer
+ {
+ private ExpressionSerializer serializer;
+
+ private ExpressionSerializer Serializer
+ => this.serializer ?? (this.serializer = GetSerializer());
+
+ ///
+ /// Automatically adds known types as array types. If set to true, sets to false.
+ ///
+ public bool? AutoAddKnownTypesAsArrayTypes { get; set; }
+
+ ///
+ /// Automatically adds known types as list types. If set to true, sets to false.
+ ///
+ public bool? AutoAddKnownTypesAsListTypes { get; set; }
+
+ public abstract Type[] GetKnownTypes();
+
+ private ExpressionSerializer GetSerializer()
+ {
+ var jsonSerializer = new JsonSerializer();
+ foreach (var type in GetKnownTypes())
+ {
+ jsonSerializer.AddKnownType(type);
+ }
+ if (this.AutoAddKnownTypesAsArrayTypes.HasValue) {
+ jsonSerializer.AutoAddKnownTypesAsArrayTypes = this.AutoAddKnownTypesAsArrayTypes.Value;
+ }
+ if (this.AutoAddKnownTypesAsListTypes.HasValue)
+ {
+ jsonSerializer.AutoAddKnownTypesAsListTypes = this.AutoAddKnownTypesAsListTypes.Value;
+ }
+
+ return new ExpressionSerializer(jsonSerializer);
+ }
+
+ public Expression Deserialize(string expressionString)
+ {
+ return this.Serializer.DeserializeText(expressionString);
+ }
+
+ public string Serialize(Expression expression)
+ {
+ return this.Serializer.SerializeText(expression);
+ }
+ }
+}
diff --git a/src/BlazorWorker.WorkerBackgroundService/WebWorkerOptions.cs b/src/BlazorWorker.WorkerBackgroundService/WebWorkerOptions.cs
index 1326d45..d816df8 100644
--- a/src/BlazorWorker.WorkerBackgroundService/WebWorkerOptions.cs
+++ b/src/BlazorWorker.WorkerBackgroundService/WebWorkerOptions.cs
@@ -1,18 +1,65 @@
-namespace BlazorWorker.WorkerBackgroundService
+using System;
+
+namespace BlazorWorker.WorkerBackgroundService
{
public class WebWorkerOptions
{
+ ///
+ /// Name of environment variable to be used for transferring the serializer typename
+ ///
+ public static readonly string ExpressionSerializerTypeEnvKey = "BLAZORWORKER_EXPRESSIONSERIALIZER";
+
private ISerializer messageSerializer;
private IExpressionSerializer expressionSerializer;
+ private Type expressionSerializerType;
+
+
public ISerializer MessageSerializer {
get => messageSerializer ?? (messageSerializer = new DefaultMessageSerializer());
set => messageSerializer = value;
}
- public IExpressionSerializer ExpressionSerializer {
- get => expressionSerializer ?? (expressionSerializer = new SerializeLinqExpressionSerializer()) ;
+ public IExpressionSerializer ExpressionSerializer {
+ get => expressionSerializer ?? (expressionSerializer = CreateSerializerInstance());
set => expressionSerializer = value;
}
+
+ public Type ExpressionSerializerType
+ {
+ get => expressionSerializerType ?? typeof(SerializeLinqExpressionSerializer);
+ set => expressionSerializerType = ValidateExpressionSerializerType(value);
+ }
+
+ ///
+ /// Ensures that the provided type implements
+ ///
+ ///
+ ///
+ private Type ValidateExpressionSerializerType(Type sourceType)
+ {
+ if (sourceType == null)
+ {
+ return null;
+ }
+
+ if (!sourceType.IsClass)
+ {
+ throw new Exception($"The {nameof(ExpressionSerializerType)} '{sourceType.AssemblyQualifiedName}' must be a class.");
+ }
+
+ if (!typeof(IExpressionSerializer).IsAssignableFrom(sourceType))
+ {
+ throw new Exception($"The {nameof(ExpressionSerializerType)} '{sourceType.AssemblyQualifiedName}' must be assignable to {nameof(IExpressionSerializer)}");
+ }
+
+ return sourceType;
+ }
+
+ private IExpressionSerializer CreateSerializerInstance()
+ {
+ var instance = Activator.CreateInstance(ExpressionSerializerType);
+ return (IExpressionSerializer)instance;
+ }
}
}
diff --git a/src/BlazorWorker.WorkerBackgroundService/WorkerInstanceManager.cs b/src/BlazorWorker.WorkerBackgroundService/WorkerInstanceManager.cs
index 79e943f..b0cb6fa 100644
--- a/src/BlazorWorker.WorkerBackgroundService/WorkerInstanceManager.cs
+++ b/src/BlazorWorker.WorkerBackgroundService/WorkerInstanceManager.cs
@@ -13,12 +13,12 @@ namespace BlazorWorker.WorkerBackgroundService
public partial class WorkerInstanceManager
{
private readonly ConcurrentDictionary events =
- new ConcurrentDictionary();
+ new();
private static readonly MessageHandlerRegistry messageHandlerRegistry
- = new MessageHandlerRegistry(wim => wim.serializer);
+ = new(wim => wim.serializer);
- public static readonly WorkerInstanceManager Instance = new WorkerInstanceManager();
+ public static readonly WorkerInstanceManager Instance = new();
internal readonly ISerializer serializer;
private readonly WebWorkerOptions options;
@@ -40,6 +40,12 @@ public WorkerInstanceManager()
{
this.serializer = new DefaultMessageSerializer();
this.options = new WebWorkerOptions();
+ var expressionSerializerType = Environment.GetEnvironmentVariable(WebWorkerOptions.ExpressionSerializerTypeEnvKey);
+ if (expressionSerializerType != null)
+ {
+ this.options.ExpressionSerializerType = Type.GetType(expressionSerializerType);
+ }
+
this.simpleInstanceService = SimpleInstanceService.Instance;
this.messageHandler = messageHandlerRegistry.GetRegistryForInstance(this);
diff --git a/src/BlazorWorker.WorkerCore/InitOptions.cs b/src/BlazorWorker.WorkerCore/InitOptions.cs
index f13db69..8cf27e6 100644
--- a/src/BlazorWorker.WorkerCore/InitOptions.cs
+++ b/src/BlazorWorker.WorkerCore/InitOptions.cs
@@ -14,7 +14,7 @@ public class WorkerInitOptions
///
public WorkerInitOptions()
{
- DependentAssemblyFilenames = new string[] { };
+ DependentAssemblyFilenames = [];
#if NETSTANDARD21
DeployPrefix = "_framework/_bin";
@@ -49,7 +49,7 @@ public WorkerInitOptions()
public string DeployPrefix { get; }
///
- /// Specifieds the location of the wasm binary
+ /// Specifies the location of the wasm binary
///
public string WasmRoot { get; }
@@ -70,7 +70,7 @@ public WorkerInitOptions()
public MethodIdentifier MessageEndPoint { get; set; }
///
- /// Mono-wasm-annotated endpoint for instanciating the worker. Experts only.
+ /// Mono-wasm-annotated endpoint for instantiating the worker. Experts only.
///
public MethodIdentifier InitEndPoint { get; set; }
@@ -119,10 +119,6 @@ public WorkerInitOptions MergeWith(WorkerInitOptions initOptions)
return new WorkerInitOptions
{
CallbackMethod = initOptions.CallbackMethod ?? this.CallbackMethod,
- /*DependentAssemblyFilenames = this.DependentAssemblyFilenames
- .Concat(initOptions.DependentAssemblyFilenames)
- .Distinct()
- .ToArray(),*/
UseConventionalServiceAssembly = initOptions.UseConventionalServiceAssembly,
MessageEndPoint = initOptions.MessageEndPoint ?? this.MessageEndPoint,
InitEndPoint = initOptions.InitEndPoint ?? this.InitEndPoint,
@@ -139,7 +135,7 @@ public static class WorkerInitOptionsExtensions
{
///
- /// Adds the specified assembly filesnames as dependencies
+ /// Adds the specified assembly filenames as dependencies
///
///
///
@@ -160,7 +156,6 @@ public static WorkerInitOptions AddAssemblies(this WorkerInitOptions source, par
[Obsolete("Manual dependency optimization is silently ignored in this version of BlazorWorker.")]
public static WorkerInitOptions AddConventionalAssemblyOfService(this WorkerInitOptions source)
{
- source.UseConventionalServiceAssembly = true;
return source;
}
@@ -172,7 +167,7 @@ public static WorkerInitOptions AddConventionalAssemblyOfService(this WorkerInit
[Obsolete("Manual dependency optimization is silently ignored in this version of BlazorWorker.")]
public static WorkerInitOptions AddAssemblyOf(this WorkerInitOptions source)
{
- return source.AddAssemblyOfType(typeof(T));
+ return source;
}
///
@@ -184,12 +179,11 @@ public static WorkerInitOptions AddAssemblyOf(this WorkerInitOptions source)
[Obsolete("Manual dependency optimization is silently ignored in this version of BlazorWorker.")]
public static WorkerInitOptions AddAssemblyOfType(this WorkerInitOptions source, Type type)
{
- source.AddAssemblies($"{type.Assembly.GetName().Name}.dll");
return source;
}
///
- /// Registers the neccessary dependencies for injecting or instanciating in the background service.
+ /// Registers the necessary dependencies for injecting or instantiating in the background service.
///
///
///
@@ -197,21 +191,6 @@ public static WorkerInitOptions AddAssemblyOfType(this WorkerInitOptions source,
[Obsolete("Manual dependency optimization is silently ignored in this version of BlazorWorker.")]
public static WorkerInitOptions AddHttpClient(this WorkerInitOptions source)
{
-#if NETSTANDARD21
- source.AddAssemblies("System.Net.Http.dll", "System.Net.Http.WebAssemblyHttpHandler.dll");
-#endif
-
-#if NET5_0_OR_GREATER
- source.AddAssemblies(
- "System.Net.Http.dll",
- "System.Security.Cryptography.X509Certificates.dll",
- "System.Net.Primitives.dll",
- "System.Net.Requests.dll",
- "System.Net.Security.dll",
- "System.Net.dll",
- "System.Diagnostics.Tracing.dll");
-#endif
-
return source;
}