Skip to content

Commit 4194f50

Browse files
committed
Removed all static code in favor of new interop model
1 parent 6cbf79d commit 4194f50

File tree

8 files changed

+139
-203
lines changed

8 files changed

+139
-203
lines changed
Lines changed: 28 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,40 @@
11
import * as signalR from "@aspnet/signalr";
22
import * as sianglRMessagePack from "@aspnet/signalr-protocol-msgpack";
3+
import { HttpTransportType, LogLevel } from "@aspnet/signalr";
34

45
type DotNetType = {
56
invokeMethod<T>(assemblyName: string, methodIdentifier: string, ...args: any[]): T,
67
invokeMethodAsync<T>(assemblyName: string, methodIdentifier: string, ...args: any[]): Promise<T>
78
}
89

9-
const BlazorExtensionsSignalrAssembly = 'Blazor.Extensions.SignalR';
10+
type DotNetReferenceType = {
11+
invokeMethod<T>(methodIdentifier: string, ...args: any[]): T,
12+
invokeMethodAsync<T>(methodIdentifier: string, ...args: any[]): Promise<T>
13+
}
14+
1015
const DotNet: DotNetType = window["DotNet"];
1116

1217
export class HubConnectionManager {
1318
private _hubConnections: Map<string, signalR.HubConnection> = new Map<string, signalR.HubConnection>();
1419
private _handles: Map<string, (payload: any) => Promise<void>> = new Map<string, (payload: any) => Promise<void>>();
1520

16-
public CreateConnection = (connectionId: string, httpConnectionOptions: any) => {
21+
public CreateConnection = (connectionId: string, httpConnectionOptions: DotNetReferenceType) => {
1722
if (!connectionId) throw new Error('Invalid connectionId.');
1823
if (!httpConnectionOptions) throw new Error('Invalid transport options.');
19-
if (!httpConnectionOptions.url) throw new Error('Invalid hub url.');
24+
let url = httpConnectionOptions.invokeMethod<string>('get_Url');
25+
if (!url) throw new Error('Invalid hub url.');
2026

2127
let options: any = {
22-
logger: httpConnectionOptions.logLevel,
23-
transport: httpConnectionOptions.transport,
24-
logMessageContent: httpConnectionOptions.logMessageContent,
25-
skipNegotiation: httpConnectionOptions.skipNegotiation
28+
logger: httpConnectionOptions.invokeMethod<LogLevel>('get_LogLevel'),
29+
transport: httpConnectionOptions.invokeMethod<HttpTransportType>('get_Transport'),
30+
logMessageContent: httpConnectionOptions.invokeMethod<boolean>('get_LogMessageContent'),
31+
skipNegotiation: httpConnectionOptions.invokeMethod<boolean>('get_SkipNegotiation')
2632
};
2733

28-
if (httpConnectionOptions.hasAccessTokenFactory) {
34+
if (httpConnectionOptions.invokeMethod<true>('HasAccessTokenFactory')) {
2935
options.accessTokenFactory = () => {
3036
return new Promise<string>(async (resolve, reject) => {
31-
const token = await DotNet.invokeMethodAsync<string>(BlazorExtensionsSignalrAssembly, 'GetAccessToken', connectionId);
32-
37+
const token = await httpConnectionOptions.invokeMethodAsync<string>('GetAccessToken');
3338
if (token) {
3439
resolve(token);
3540
} else {
@@ -42,9 +47,9 @@ export class HubConnectionManager {
4247
if (this._hubConnections[connectionId]) return;
4348

4449
let connectionBuilder = new signalR.HubConnectionBuilder()
45-
.withUrl(httpConnectionOptions.url, options);
50+
.withUrl(url, options);
4651

47-
if (httpConnectionOptions.addMessagePack) {
52+
if (httpConnectionOptions.invokeMethod<true>('get_EnableMessagePack')) {
4853
connectionBuilder
4954
.withHubProtocol(new sianglRMessagePack.MessagePackHubProtocol());
5055
}
@@ -58,11 +63,7 @@ export class HubConnectionManager {
5863

5964
public StartConnection = (connectionId: string): Promise<void> => {
6065
const connection = this.GetConnection(connectionId);
61-
62-
connection.onclose(async err => {
63-
await DotNet.invokeMethodAsync(BlazorExtensionsSignalrAssembly, 'OnClose', connectionId, JSON.stringify(err));
64-
});
65-
66+
6667
return connection.start();
6768
}
6869

@@ -90,14 +91,14 @@ export class HubConnectionManager {
9091
return connection;
9192
}
9293

93-
private On = (connectionId: string, methodName: string, handleId: string) => {
94+
public On = (connectionId: string, callback: DotNetReferenceType) => {
9495
const connection = this.GetConnection(connectionId);
95-
const handle = (payload) => this.OnHandler(connectionId, methodName, handleId, payload);
96-
this._handles.set(handleId, handle);
97-
connection.on(methodName, handle);
96+
const handle = (payload) => callback.invokeMethodAsync<void>('On', JSON.stringify(payload));
97+
this._handles.set(callback.invokeMethod<string>('get_Id'), handle);
98+
connection.on(callback.invokeMethod<string>('get_MethodName'), handle);
9899
}
99100

100-
private Off = (connectionId: string, methodName: string, handleId: string) => {
101+
public Off = (connectionId: string, methodName: string, handleId: string) => {
101102
const connection = this.GetConnection(connectionId);
102103
const handle = this._handles.get(handleId);
103104
if (handle) {
@@ -106,38 +107,10 @@ export class HubConnectionManager {
106107
}
107108
}
108109

109-
private OnHandler = async (connectionId: string, methodName: string, handleId: string, payload: any) => {
110-
await DotNet.invokeMethodAsync(BlazorExtensionsSignalrAssembly, 'Dispatch', connectionId, methodName, handleId, JSON.stringify(payload));
111-
}
112-
113-
public static initialize() {
114-
//const Blazor: BlazorType = window["Blazor"];
115-
//window["BlazorExtensions"].HubConnectionManager = new HubConnectionManager();
116-
117-
//Blazor.registerFunction('Blazor.Extensions.SignalR.InvokeAsync', (connectionId: string, methodName: string, args: any) => {
118-
// //TODO remove this parse after Blazor fixed the async args json parsing code
119-
// const parsedConnectionId = JSON.parse(connectionId);
120-
// const parsedMethodName = JSON.parse(methodName);
121-
// const parsedArgs = JSON.parse(args);
122-
123-
// return window["BlazorExtensions"].HubConnectionManager.invokeAsync(parsedConnectionId, parsedMethodName, ...parsedArgs);
124-
//});
125-
126-
//Blazor.registerFunction('Blazor.Extensions.SignalR.InvokeWithResultAsync', (connectionId: string, methodName: string, args: any) => {
127-
// //TODO remove this parse after Blazor fixed the async args json parsing code
128-
// const parsedConnectionId = JSON.parse(connectionId);
129-
// const parsedMethodName = JSON.parse(methodName);
130-
// const parsedArgs = JSON.parse(args);
131-
132-
// return window["BlazorExtensions"].HubConnectionManager.invokeWithResultAsync(parsedConnectionId, parsedMethodName, ...parsedArgs);
133-
//});
134-
135-
//Blazor.registerFunction('Blazor.Extensions.SignalR.On', (connectionId: string, methodName: string, handleId: string) => {
136-
// return window["BlazorExtensions"].HubConnectionManager.on(connectionId, methodName, handleId);
137-
//});
138-
139-
//Blazor.registerFunction('Blazor.Extensions.SignalR.Off', (connectionId: string, methodName: string, handleId: string) => {
140-
// return window["BlazorExtensions"].HubConnectionManager.off(connectionId, methodName, handleId);
141-
//});
110+
public OnClose = (connectionId: string, onErrorCallback: DotNetReferenceType) => {
111+
const connection = this.GetConnection(connectionId);
112+
connection.onclose(async err => {
113+
onErrorCallback.invokeMethodAsync<void>('OnClose', JSON.stringify(err));
114+
});
142115
}
143116
}
Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,23 @@
1+
using Microsoft.JSInterop;
12
using System;
2-
using System.Runtime.Serialization;
33
using System.Threading.Tasks;
44

55
namespace Blazor.Extensions
66
{
77
public class HttpConnectionOptions
8-
{
9-
public HttpTransportType Transport { get; set; }
10-
public SignalRLogLevel LogLevel { get; set; }
11-
public bool LogMessageContent { get; set; }
12-
public bool SkipNegotiation { get; set; }
13-
internal bool EnableMessagePack { get; set; }
14-
internal string Url { get; set; }
8+
{
9+
public HttpTransportType Transport { [JSInvokable]get; set; }
10+
public SignalRLogLevel LogLevel { [JSInvokable]get; set; }
11+
public bool LogMessageContent { [JSInvokable]get; set; }
12+
public bool SkipNegotiation { [JSInvokable]get; set; }
13+
public bool EnableMessagePack { [JSInvokable]get; set; }
14+
public string Url { [JSInvokable]get; set; }
1515
public Func<Task<string>> AccessTokenProvider { get; set; }
16-
}
1716

18-
internal class InternalHttpConnectionOptions
19-
{
20-
public HttpTransportType Transport { get; set; }
21-
public SignalRLogLevel LogLevel { get; set; }
22-
public bool LogMessageContent { get; set; }
23-
public bool SkipNegotiation { get; set; }
24-
public bool EnableMessagePack { get; set; }
25-
public string Url { get; set; }
26-
public bool HasAccessTokenFactory { get; set; }
17+
[JSInvokable]
18+
public Task<string> GetAccessToken() => this.AccessTokenProvider?.Invoke();
2719

28-
public InternalHttpConnectionOptions(HttpConnectionOptions options)
29-
{
30-
this.Transport = options.Transport;
31-
this.LogLevel = options.LogLevel;
32-
this.LogMessageContent = options.LogMessageContent;
33-
this.SkipNegotiation = options.SkipNegotiation;
34-
this.EnableMessagePack = options.EnableMessagePack;
35-
this.Url = options.Url;
36-
this.HasAccessTokenFactory = options.AccessTokenProvider != null;
37-
}
20+
[JSInvokable]
21+
public bool HasAccessTokenFactory() => this.AccessTokenProvider != null;
3822
}
3923
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.JSInterop;
2+
using System;
3+
using System.Threading.Tasks;
4+
5+
namespace Blazor.Extensions
6+
{
7+
public class HubCloseCallback
8+
{
9+
private readonly Func<Exception, Task> _callback;
10+
11+
public HubCloseCallback(Func<Exception, Task> closeCallback)
12+
{
13+
this._callback = closeCallback;
14+
}
15+
16+
[JSInvokable]
17+
public Task OnClose(string error) => this._callback(new Exception(error));
18+
}
19+
}

src/Blazor.Extensions.SignalR/HubConnection.cs

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ namespace Blazor.Extensions
77
{
88
public class HubConnection : IDisposable
99
{
10+
private const string ON_METHOD = "BlazorExtensions.SignalR.On";
11+
private const string ON_CLOSE_METHOD = "BlazorExtensions.SignalR.OnClose";
12+
private const string OFF_METHOD = "BlazorExtensions.SignalR.Off";
13+
private const string CREATE_CONNECTION_METHOD = "BlazorExtensions.SignalR.CreateConnection";
14+
private const string REMOVE_CONNECTION_METHOD = "BlazorExtensions.SignalR.RemoveConnection";
1015
private const string START_CONNECTION_METHOD = "BlazorExtensions.SignalR.StartConnection";
1116
private const string STOP_CONNECTION_METHOD = "BlazorExtensions.SignalR.StopConnection";
1217
private const string INVOKE_ASYNC_METHOD = "BlazorExtensions.SignalR.InvokeAsync";
@@ -15,19 +20,19 @@ public class HubConnection : IDisposable
1520
internal HttpConnectionOptions Options { get; }
1621
internal string InternalConnectionId { get; }
1722

18-
private Dictionary<string, Dictionary<string, (SubscriptionHandle, Func<string, Task>)>> _handlers = new Dictionary<string, Dictionary<string, (SubscriptionHandle, Func<string, Task>)>>();
19-
20-
private Func<Exception, Task> _errorHandler;
23+
private Dictionary<string, Dictionary<string, HubMethodCallback>> _callbacks = new Dictionary<string, Dictionary<string, HubMethodCallback>>();
24+
25+
private HubCloseCallback _closeCallback;
2126

2227
public HubConnection(HttpConnectionOptions options)
2328
{
2429
this.Options = options;
2530
this.InternalConnectionId = Guid.NewGuid().ToString();
26-
HubConnectionManager.AddConnection(this);
31+
((IJSInProcessRuntime)JSRuntime.Current).Invoke<object>(CREATE_CONNECTION_METHOD,
32+
this.InternalConnectionId,
33+
new DotNetObjectRef(this.Options));
2734
}
2835

29-
internal Task<string> GetAccessToken() => this.Options.AccessTokenProvider != null ? this.Options.AccessTokenProvider() : null;
30-
internal Task OnClose(string error) => this._errorHandler != null ? this._errorHandler(new Exception(error)) : Task.CompletedTask;
3136

3237
public Task StartAsync() => JSRuntime.Current.InvokeAsync<object>(START_CONNECTION_METHOD, this.InternalConnectionId);
3338
public Task StopAsync() => JSRuntime.Current.InvokeAsync<object>(STOP_CONNECTION_METHOD, this.InternalConnectionId);
@@ -37,73 +42,65 @@ public IDisposable On<TResult>(string methodName, Func<TResult, Task> handler)
3742
if (string.IsNullOrEmpty(methodName)) throw new ArgumentNullException(nameof(methodName));
3843
if (handler == null) throw new ArgumentNullException(nameof(handler));
3944

40-
var subscriptionHandle = new SubscriptionHandle(methodName, this);
41-
if (this._handlers.TryGetValue(methodName, out var methodHandlers))
45+
var callbackId = Guid.NewGuid().ToString();
46+
47+
var callback = new HubMethodCallback(callbackId, methodName, this,
48+
(json) =>
49+
{
50+
var payload = Json.Deserialize<TResult>(json);
51+
return handler(payload);
52+
}
53+
);
54+
55+
if (this._callbacks.TryGetValue(methodName, out var methodHandlers))
4256
{
43-
methodHandlers[subscriptionHandle.HandleId] =
44-
(subscriptionHandle, (json) =>
45-
{
46-
var payload = Json.Deserialize<TResult>(json);
47-
return handler(payload);
48-
}
49-
);
57+
methodHandlers[callback.Id] = callback;
5058
}
5159
else
5260
{
53-
this._handlers[methodName] = new Dictionary<string, (SubscriptionHandle, Func<string, Task>)>
61+
this._callbacks[methodName] = new Dictionary<string, HubMethodCallback>
5462
{
55-
{
56-
subscriptionHandle.HandleId,
57-
(subscriptionHandle, (json) =>
58-
{
59-
var payload = Json.Deserialize<TResult>(json);
60-
return handler(payload);
61-
})
62-
}
63+
{ callback.Id, callback }
6364
};
6465
}
6566

66-
HubConnectionManager.On(this.InternalConnectionId, subscriptionHandle);
67-
return subscriptionHandle;
67+
((IJSInProcessRuntime)JSRuntime.Current).Invoke<object>(ON_METHOD, this.InternalConnectionId, new DotNetObjectRef(callback));
68+
69+
//HubConnectionManager.On(this.InternalConnectionId, callback);
70+
return callback;
6871
}
6972

70-
internal void RemoveHandle(string methodName, string handleId)
73+
internal void RemoveHandle(string methodName, string callbackId)
7174
{
72-
if (this._handlers.TryGetValue(methodName, out var handlers))
75+
if (this._callbacks.TryGetValue(methodName, out var callbacks))
7376
{
74-
if (handlers.TryGetValue(handleId, out var handle))
77+
if (callbacks.TryGetValue(callbackId, out var callback))
7578
{
76-
HubConnectionManager.Off(this.InternalConnectionId, handle.Item1);
77-
handlers.Remove(handleId);
79+
((IJSInProcessRuntime)JSRuntime.Current).Invoke<object>(OFF_METHOD, this.InternalConnectionId, new DotNetObjectRef(callback));
80+
//HubConnectionManager.Off(this.InternalConnectionId, handle.Item1);
81+
callbacks.Remove(callbackId);
7882

79-
if (handlers.Count == 0)
83+
if (callbacks.Count == 0)
8084
{
81-
this._handlers.Remove(methodName);
85+
this._callbacks.Remove(methodName);
8286
}
8387
}
8488
}
8589
}
8690

87-
public void OnClose(Func<Exception, Task> handler) => this._errorHandler = handler;
91+
public void OnClose(Func<Exception, Task> callback) {
92+
this._closeCallback = new HubCloseCallback(callback);
93+
((IJSInProcessRuntime)JSRuntime.Current).Invoke<object>(ON_CLOSE_METHOD,
94+
this.InternalConnectionId,
95+
new DotNetObjectRef(this._closeCallback));
96+
}
8897

8998
public Task InvokeAsync(string methodName, params object[] args) =>
9099
JSRuntime.Current.InvokeAsync<object>(INVOKE_ASYNC_METHOD, this.InternalConnectionId, methodName, args);
91100

92101
public Task<TResult> InvokeAsync<TResult>(string methodName, params object[] args) =>
93102
JSRuntime.Current.InvokeAsync<TResult>(INVOKE_WITH_RESULT_ASYNC_METHOD, this.InternalConnectionId, methodName, args);
94103

95-
internal Task Dispatch(string methodName, string handleId, string payload)
96-
{
97-
if (this._handlers.TryGetValue(methodName, out var handlers))
98-
{
99-
if (handlers.TryGetValue(handleId, out var handle))
100-
{
101-
return handle.Item2(payload);
102-
}
103-
}
104-
return Task.CompletedTask;
105-
}
106-
107-
public void Dispose() => HubConnectionManager.RemoveConnection(this.InternalConnectionId);
104+
public void Dispose() => ((IJSInProcessRuntime)JSRuntime.Current).Invoke<object>(REMOVE_CONNECTION_METHOD, this.InternalConnectionId);
108105
}
109106
}

src/Blazor.Extensions.SignalR/HubConnectionBuilder.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ public class HubConnectionBuilder
88

99
internal HttpConnectionOptions Options { get; set; } = new HttpConnectionOptions();
1010

11+
/// <summary>
12+
/// Build a SignalR <see cref="HubConnection"/>
13+
/// This method can only be called once.
14+
/// </summary>
15+
/// <returns>Return a <see cref="HubConnection"/>.</returns>
1116
public HubConnection Build()
1217
{
1318
// Build can only be used once

0 commit comments

Comments
 (0)