Skip to content

Commit d11c650

Browse files
authored
Enables a custom expression serializer, and… (#98)
Enables a custom expression serializer, and also a baseclass for just adding known types to the Serialize.Linq JsonSerialize
1 parent 88a5fa5 commit d11c650

File tree

12 files changed

+384
-37
lines changed

12 files changed

+384
-37
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@page "/ComplexSerialization"
2+
<BlazorWorker.Demo.SharedPages.Pages.ComplexSerialization />
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
@inject IWorkerFactory workerFactory
2+
3+
@using BlazorWorker.BackgroundServiceFactory
4+
@using BlazorWorker.WorkerBackgroundService
5+
@using BlazorWorker.Demo.Shared
6+
@using BlazorWorker.Core
7+
<div class="row">
8+
<div class="col-6 col-xs-12">
9+
<h1>.NET Worker Multithreading</h1>
10+
11+
Complex / Custom Serialization <br />
12+
<br /><br />
13+
<button @onclick="OnClick" class="btn btn-primary">Run Test</button><br />
14+
15+
<br />
16+
<br />
17+
<strong>Output:</strong>
18+
<hr />
19+
<pre>
20+
@output
21+
</pre>
22+
</div>
23+
<div class="col-6 col-xs-12">
24+
<GithubSource RelativePath="Pages/BackgroundServiceMulti.razor" />
25+
</div>
26+
</div>
27+
@code {
28+
29+
string output;
30+
31+
IWorker worker;
32+
IWorkerBackgroundService<ComplexService> backgroundService;
33+
34+
string RunDisabled => Running ? "disabled" : null;
35+
bool Running = false;
36+
37+
38+
public class ComplexService
39+
{
40+
public ComplexServiceResponse ComplexCall(ComplexServiceArg arg)
41+
{
42+
return new ComplexServiceResponse
43+
{
44+
OriginalArg = arg,
45+
OnlyInResponse = "This is only in response."
46+
};
47+
}
48+
}
49+
50+
public class ComplexServiceArg
51+
{
52+
public string ThisIsJustAString { get; set; }
53+
public OhLookARecord ARecord { get; set; }
54+
public Dictionary<string, string> ADictionary { get; set; }
55+
public ComplexServiceArg TypeRecursive { get; set; }
56+
}
57+
58+
public class ComplexServiceResponse
59+
{
60+
public ComplexServiceArg OriginalArg { get; set; }
61+
public string OnlyInResponse { get; set; }
62+
}
63+
64+
public record OhLookARecord
65+
{
66+
public int Number { get; set; }
67+
}
68+
69+
public async Task OnClick(EventArgs _)
70+
{
71+
Running = true;
72+
await this.InvokeAsync(StateHasChanged);
73+
74+
output = "";
75+
var rn = Environment.NewLine;
76+
try
77+
{
78+
if (worker == null)
79+
{
80+
output += $"Starting worker...{rn}";
81+
await this.InvokeAsync(StateHasChanged);
82+
worker = await workerFactory.CreateAsync();
83+
}
84+
if (backgroundService == null)
85+
{
86+
87+
output += $"Starting BackgroundService...{rn}";
88+
await this.InvokeAsync(StateHasChanged);
89+
/*
90+
* have a look here. This is the essence of this example.
91+
* */
92+
93+
backgroundService = await worker.CreateBackgroundServiceAsync<ComplexService>(options
94+
=> options.UseCustomExpressionSerializer(typeof(CustomSerializeLinqExpressionJsonSerializer)));
95+
}
96+
97+
var sw = System.Diagnostics.Stopwatch.StartNew();
98+
99+
var complexArgInstance = new ComplexServiceArg
100+
{
101+
ThisIsJustAString = "just a string",
102+
ARecord = new OhLookARecord { Number = 5 },
103+
ADictionary = new Dictionary<string, string>
104+
{
105+
{ "Test", "TestValue" }
106+
},
107+
TypeRecursive = new ComplexServiceArg
108+
{
109+
ThisIsJustAString = "SubString"
110+
}
111+
};
112+
113+
var result = await backgroundService.RunAsync(service => service.ComplexCall(complexArgInstance));
114+
var elapsed = sw.ElapsedMilliseconds;
115+
output += $"{rn}result: " + System.Text.Json.JsonSerializer.Serialize(result);
116+
output += $"{rn}roundtrip to worker in {elapsed}ms";
117+
}
118+
catch (Exception e)
119+
{
120+
output += $"{rn}Error = {e}";
121+
}
122+
finally
123+
{
124+
Running = false;
125+
output += $"{rn}Done.";
126+
}
127+
}
128+
129+
private string LogDate()
130+
{
131+
return DateTime.Now.ToString("HH:mm:ss:fff");
132+
}
133+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
/// <summary>
10+
/// Example 1: Simple custom expression Serializer using <see cref="Serialize.Linq.ExpressionSerializer"/>
11+
/// as base class, but explicitly adds complex types as known types.
12+
/// </summary>
13+
public class CustomSerializeLinqExpressionJsonSerializer : SerializeLinqExpressionJsonSerializerBase
14+
{
15+
public override Type[] GetKnownTypes() =>
16+
[typeof(ComplexServiceArg), typeof(ComplexServiceResponse), typeof(OhLookARecord)];
17+
}
18+
19+
/// <summary>
20+
/// Fully custom Expression Serializer, which uses <see cref="Serialize.Linq.ExpressionSerializer"/> but you could use an alternative implementation.
21+
/// </summary>
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+
}

src/BlazorWorker.Demo/SharedPages/Shared/NavMenuLinksModel.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.AspNetCore.Components;
1+
using BlazorWorker.Demo.SharedPages.Pages;
2+
using Microsoft.AspNetCore.Components;
23
using Microsoft.AspNetCore.Components.Routing;
34
using System;
45
using System.Collections.Generic;
@@ -34,6 +35,10 @@ public class NavMenuLinksModel
3435
{
3536
new() { Icon = "document", Href="IndexedDB", Text = "IndexedDB" }
3637
},
38+
{
39+
new() { Icon = "document", Href="ComplexSerialization", Text = "ComplexSerialization" }
40+
},
41+
3742
{
3843
new() { Icon = "fork", Href="https://github.com/tewr/BlazorWorker", Text = "To the source!" }
3944
},

src/BlazorWorker.ServiceFactory/BlazorWorker.BackgroundServiceFactory.xml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/BlazorWorker.ServiceFactory/WorkerBackgroundServiceExtensions.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@ public static async Task<IWorkerBackgroundService<T>> CreateBackgroundServiceAsy
2626
{
2727
throw new ArgumentNullException(nameof(webWorkerProxy));
2828
}
29-
30-
var proxy = new WorkerBackgroundServiceProxy<T>(webWorkerProxy, new WebWorkerOptions());
3129
if (workerInitOptions == null)
3230
{
3331
workerInitOptions = new WorkerInitOptions();
3432
}
3533

34+
var webWorkerOptions = new WebWorkerOptions();
35+
webWorkerOptions.SetExpressionSerializerFromInitOptions(workerInitOptions);
36+
37+
var proxy = new WorkerBackgroundServiceProxy<T>(webWorkerProxy, webWorkerOptions);
38+
39+
3640
await proxy.InitAsync(workerInitOptions);
3741
return proxy;
3842
}
@@ -68,14 +72,32 @@ public static async Task<IWorkerBackgroundService<TService>> CreateBackgroundSer
6872
{
6973
workerInitOptionsModifier(workerInitOptions);
7074
}
71-
var factoryProxy = new WorkerBackgroundServiceProxy<TFactory>(webWorkerProxy, new WebWorkerOptions());
75+
76+
if (workerInitOptions == null)
77+
{
78+
workerInitOptions = new WorkerInitOptions();
79+
}
80+
81+
var webWorkerOptions = new WebWorkerOptions();
82+
webWorkerOptions.SetExpressionSerializerFromInitOptions(workerInitOptions);
83+
84+
85+
var factoryProxy = new WorkerBackgroundServiceProxy<TFactory>(webWorkerProxy, webWorkerOptions);
7286
await factoryProxy.InitAsync(workerInitOptions);
7387

7488
var newProxy = await factoryProxy.InitFromFactoryAsync(factoryExpression);
7589
newProxy.Disposables.Add(factoryProxy);
7690
return newProxy;
7791
}
7892

93+
private static void SetExpressionSerializerFromInitOptions(this WebWorkerOptions target, WorkerInitOptions workerInitOptions)
94+
{
95+
if (workerInitOptions.EnvMap?.TryGetValue(WebWorkerOptions.ExpressionSerializerTypeEnvKey, out var serializerType) == true)
96+
{
97+
target.ExpressionSerializerType = Type.GetType(serializerType);
98+
}
99+
}
100+
79101
/// <summary>
80102
/// Creates a new background service using the specified <paramref name="factoryExpression"/>
81103
/// </summary>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
/// <summary>
11+
/// Sets a custom ExpressionSerializer type. Must implement <see cref="IExpressionSerializer"/>..
12+
/// </summary>
13+
/// <param name="source"></param>
14+
/// <param name="expressionSerializerType">A type that implements <see cref="IExpressionSerializer"/></param>
15+
/// <returns></returns>
16+
/// <exception cref="ArgumentNullException"></exception>
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+
}

src/BlazorWorker.WorkerBackgroundService/BlazorWorker.WorkerBackgroundService.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Serialize.Linq.Serializers;
2+
using System;
3+
using System.Linq.Expressions;
4+
5+
namespace BlazorWorker.WorkerBackgroundService
6+
{
7+
/// <summary>
8+
/// Base class for adding known types to a serializer using <see cref="Serialize.Linq.Serializers.JsonSerializer"/>.
9+
/// </summary>
10+
public abstract class SerializeLinqExpressionJsonSerializerBase : IExpressionSerializer
11+
{
12+
private ExpressionSerializer serializer;
13+
14+
private ExpressionSerializer Serializer
15+
=> this.serializer ?? (this.serializer = GetSerializer());
16+
17+
/// <summary>
18+
/// Automatically adds known types as array types. If set to <c>true</c>, sets <see cref="AutoAddKnownTypesAsListTypes"/> to <c>false</c>.
19+
/// </summary>
20+
public bool? AutoAddKnownTypesAsArrayTypes { get; set; }
21+
22+
/// <summary>
23+
/// Automatically adds known types as list types. If set to <c>true</c>, sets <see cref="AutoAddKnownTypesAsArrayTypes"/> to <c>false</c>.
24+
/// </summary>
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+
}

0 commit comments

Comments
 (0)