Skip to content

Commit 4546099

Browse files
committed
Tweaked log levels, Added setting to attempt to automatically enable service broker on startup, added a setting to configure table names per hub.
1 parent 52cdf8c commit 4546099

File tree

14 files changed

+165
-82
lines changed

14 files changed

+165
-82
lines changed

demo/DemoServer/ChatHub.cs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,44 @@
55

66
namespace DemoServer
77
{
8-
public class ChatHub : Hub
8+
public class ChatHubA : Hub
99
{
10-
private readonly ILogger<ChatHub> _logger;
10+
private readonly ILogger<ChatHubA> _logger;
1111

12-
public ChatHub(ILogger<ChatHub> logger)
12+
public ChatHubA(ILogger<ChatHubA> logger)
13+
{
14+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
15+
}
16+
17+
private string groupId = Guid.Empty.ToString();
18+
19+
public async Task SendMessage(string name, string message)
20+
{
21+
_logger.LogInformation($"{nameof(SendMessage)} called. ConnectionId:{Context.ConnectionId}, Name:{name}, Message:{message}");
22+
await Clients.Group(groupId).SendAsync("BroadcastMessage", name, message);
23+
}
24+
25+
public override async Task OnConnectedAsync()
26+
{
27+
_logger.LogInformation($"{nameof(OnConnectedAsync)} called.");
28+
29+
await base.OnConnectedAsync();
30+
await Groups.AddToGroupAsync(Context.ConnectionId, Guid.Empty.ToString());
31+
}
32+
33+
public override async Task OnDisconnectedAsync(Exception exception)
34+
{
35+
_logger.LogInformation(exception, $"{nameof(OnDisconnectedAsync)} called.");
36+
37+
await base.OnDisconnectedAsync(exception);
38+
await Groups.RemoveFromGroupAsync(Context.ConnectionId, Guid.Empty.ToString());
39+
}
40+
}
41+
public class ChatHubB : Hub
42+
{
43+
private readonly ILogger<ChatHubB> _logger;
44+
45+
public ChatHubB(ILogger<ChatHubB> logger)
1346
{
1447
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
1548
}

demo/DemoServer/Pages/Index.cshtml

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,41 @@
11
@page
2-
<div class="container">
3-
<div class="row">&nbsp;</div>
4-
<div class="row">
5-
<div class="col-2">User</div>
6-
<div class="col-4"><input type="text" id="userInput" /></div>
7-
</div>
8-
<div class="row">
9-
<div class="col-2">Message</div>
10-
<div class="col-4"><input type="text" id="messageInput" /></div>
11-
</div>
12-
<div class="row">&nbsp;</div>
13-
<div class="row">
2+
3+
<script src="~/lib/microsoft/signalr/dist/browser/signalr.js"></script>
4+
<script src="~/js/chat.js"></script>
5+
<div class="row">
6+
@foreach (var suffix in new[] { "A", "B" })
7+
{
148
<div class="col-6">
15-
<input type="button" id="sendButton" value="Send Message" />
9+
10+
<h1>Hub @suffix</h1>
11+
<div class="container">
12+
<div class="row">&nbsp;</div>
13+
<div class="row">
14+
<div class="col-2">User</div>
15+
<div class="col-4"><input type="text" id="userInput@(suffix)" /></div>
16+
</div>
17+
<div class="row">
18+
<div class="col-2">Message</div>
19+
<div class="col-4"><input type="text" id="messageInput@(suffix)" /></div>
20+
</div>
21+
<div class="row">&nbsp;</div>
22+
<div class="row">
23+
<div class="col-6">
24+
<input type="button" id="sendButton@(suffix)" value="Send Message" />
25+
</div>
26+
</div>
27+
</div>
28+
<div class="row">
29+
<div class="col-12">
30+
<hr />
31+
</div>
32+
</div>
33+
<div class="row">
34+
<div class="col-6">
35+
<ul id="messagesList@(suffix)"></ul>
36+
</div>
37+
</div>
1638
</div>
17-
</div>
18-
</div>
19-
<div class="row">
20-
<div class="col-12">
21-
<hr />
22-
</div>
39+
<script>setupHub('@suffix')</script>
40+
}
2341
</div>
24-
<div class="row">
25-
<div class="col-6">
26-
<ul id="messagesList"></ul>
27-
</div>
28-
</div>
29-
<script src="~/lib/microsoft/signalr/dist/browser/signalr.js"></script>
30-
<script src="~/js/chat.js"></script>

demo/DemoServer/Program.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,15 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
2626
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
2727
.AddJsonFile("appsettings.localhost.json", optional: true, reloadOnChange: true)
2828
.AddEnvironmentVariables()
29-
);
29+
)
30+
.ConfigureLogging(builder =>
31+
{
32+
builder.AddSystemdConsole(options =>
33+
{
34+
options.IncludeScopes = true;
35+
options.TimestampFormat = "hh:mm:ss ";
36+
});
37+
});
3038
});
3139
}
3240
}

demo/DemoServer/Startup.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ public void ConfigureServices(IServiceCollection services)
2626
{
2727
services.AddRazorPages();
2828
services.AddSignalR()
29-
.AddSqlServer(Configuration.GetConnectionString("Default"));
29+
.AddSqlServer(o =>
30+
{
31+
o.ConnectionString = Configuration.GetConnectionString("Default");
32+
o.AutoEnableServiceBroker = true;
33+
o.TableCount = 1;
34+
o.SchemaName = "Signalrcore";
35+
});
3036
}
3137

3238
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@@ -53,7 +59,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
5359
app.UseEndpoints(endpoints =>
5460
{
5561
endpoints.MapRazorPages();
56-
endpoints.MapHub<ChatHub>("/chatHub");
62+
endpoints.MapHub<ChatHubA>("/chatHubA");
63+
//endpoints.MapHub<ChatHubB>("/chatHubB");
5764
});
5865
}
5966
}

demo/DemoServer/appsettings.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
"LogLevel": {
44
"Default": "Information",
55
"Microsoft": "Warning",
6-
"IntelliTect.AspNetCore.SignalR": "Trace",
6+
//"IntelliTect.AspNetCore.SignalR": "Information",
7+
"IntelliTect.AspNetCore.SignalR": "Debug",
78
"Microsoft.Hosting.Lifetime": "Information"
89
}
910
},
10-
"AllowedHosts": "*"
11+
"AllowedHosts": "*",
12+
"ConnectionStrings": {
13+
"Default": "Server=localhost;Database=SignalRTestDb;Trusted_Connection=True;MultipleActiveResultSets=True;"
14+
}
1115
}

src/IntelliTect.AspNetCore.SignalR.SqlServer/IntelliTect.AspNetCore.SignalR.SqlServer.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@
1515
<ItemGroup>
1616
<EmbeddedResource Include="**/*.sql" />
1717
</ItemGroup>
18+
19+
<ItemGroup>
20+
<None Remove="Internal\SqlServer\enable-broker.sql" />
21+
</ItemGroup>
1822

1923
</Project>

src/IntelliTect.AspNetCore.SignalR.SqlServer/Internal/SqlServer/ObservableDbOperation.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ internal class ObservableDbOperation : DbOperation, IDisposable, IDbBehavior
3434
private readonly ManualResetEventSlim _stopHandle = new ManualResetEventSlim(true);
3535
private readonly IDbBehavior _dbBehavior;
3636

37-
private string _dependencyQueue = Guid.NewGuid().ToString();
37+
private string? _dependencyQueue = null;// Guid.NewGuid().ToString();
3838

3939
private volatile bool _disposing;
4040
private long _notificationState;
@@ -130,7 +130,7 @@ public void ExecuteReaderWithUpdates(Action<IDataRecord, DbOperation> processRec
130130

131131
if (recordCount > 0)
132132
{
133-
_logger.LogTrace("{0}{1} records received", TracePrefix, recordCount);
133+
_logger.LogDebug("{0}{1} records received", TracePrefix, recordCount);
134134

135135
// We got records so start the retry loop again
136136
i = -1;
@@ -154,7 +154,7 @@ public void ExecuteReaderWithUpdates(Action<IDataRecord, DbOperation> processRec
154154
// No records after all retries, set up a SQL notification
155155
try
156156
{
157-
_logger.LogTrace("{0}Setting up SQL notification", TracePrefix);
157+
_logger.LogDebug("{0}Setting up SQL notification", TracePrefix);
158158

159159
recordCount = ExecuteReader(processRecord, command =>
160160
{
@@ -165,7 +165,7 @@ public void ExecuteReaderWithUpdates(Action<IDataRecord, DbOperation> processRec
165165

166166
if (recordCount > 0)
167167
{
168-
_logger.LogTrace("{0}Records were returned by the command that sets up the SQL notification, restarting the receive loop", TracePrefix);
168+
_logger.LogDebug("{0}Records were returned by the command that sets up the SQL notification, restarting the receive loop", TracePrefix);
169169

170170
i = -1;
171171
break; // break the inner for loop
@@ -186,7 +186,7 @@ public void ExecuteReaderWithUpdates(Action<IDataRecord, DbOperation> processRec
186186
{
187187
// Failed to change _notificationState from ProcessingUpdates to AwaitingNotification, it was already NotificationReceived
188188

189-
_logger.LogTrace("{0}The SQL notification fired before the receive loop returned, restarting the receive loop", TracePrefix);
189+
_logger.LogDebug("{0}The SQL notification fired before the receive loop returned, restarting the receive loop", TracePrefix);
190190

191191
i = -1;
192192
break; // break the inner for loop
@@ -261,7 +261,7 @@ protected virtual void AddSqlDependency(IDbCommand command, Action<SqlNotificati
261261
SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sender", Justification = "Event handler")]
262262
protected virtual void SqlDependency_OnChange(SqlNotificationEventArgs e, Action<IDataRecord, DbOperation> processRecord)
263263
{
264-
Trace.TraceInformation("{0}SQL notification change fired", TracePrefix);
264+
_logger.LogDebug("{0}SQL notification change fired", TracePrefix);
265265

266266
lock (_stopLocker)
267267
{
@@ -276,7 +276,7 @@ protected virtual void SqlDependency_OnChange(SqlNotificationEventArgs e, Action
276276

277277
if (previousState == NotificationState.NotificationReceived)
278278
{
279-
Trace.TraceError("{0}Overlapping SQL change notifications received, this should never happen, BUG!", TracePrefix);
279+
_logger.LogError("{0}Overlapping SQL change notifications received, this should never happen, BUG!", TracePrefix);
280280

281281
return;
282282
}
@@ -367,21 +367,21 @@ protected virtual bool StartSqlDependencyListener()
367367
}
368368
catch (InvalidOperationException)
369369
{
370-
Trace.TraceInformation("{0}SQL Service Broker is disabled, disabling query notifications", TracePrefix);
370+
_logger.LogWarning("{0}SQL Service Broker is disabled. Falling back on periodic polling.", TracePrefix);
371371
_notificationState = NotificationState.Disabled;
372372
return false;
373373
}
374374
catch (NullReferenceException)
375375
{
376376
// Workaround for https://github.com/dotnet/SqlClient/issues/1264
377377

378-
Trace.TraceInformation("{0}SQL Service Broker is disabled, disabling query notifications", TracePrefix);
378+
_logger.LogWarning("{0}SQL Service Broker is disabled. Falling back on periodic polling.", TracePrefix);
379379
_notificationState = NotificationState.Disabled;
380380
return false;
381381
}
382382
catch (Exception ex)
383383
{
384-
Trace.TraceError("{0}Error starting SQL notification listener: {1}", TracePrefix, ex);
384+
_logger.LogError(ex, "{0}Error starting SQL notification listener", TracePrefix);
385385

386386
return false;
387387
}
@@ -405,7 +405,7 @@ protected virtual void Stop(Exception? ex)
405405
}
406406
catch (Exception stopEx)
407407
{
408-
Trace.TraceError("{0}Error occurred while stopping SQL notification listener: {1}", TracePrefix, stopEx);
408+
_logger.LogError(stopEx, "{0}Error occurred while stopping SQL notification listener: {1}", TracePrefix);
409409
}
410410
}
411411

src/IntelliTect.AspNetCore.SignalR.SqlServer/Internal/SqlServer/SqlInstaller.cs

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ namespace Microsoft.AspNet.SignalR.SqlServer
1111
internal class SqlInstaller
1212
{
1313
private const int SchemaVersion = 1;
14-
private const string SchemaTableName = "Schema";
1514

1615
private readonly string _messagesTableNamePrefix;
1716
private readonly ILogger _logger;
@@ -29,43 +28,34 @@ public void Install()
2928
{
3029
_logger.LogInformation("Start installing SignalR SQL objects");
3130

32-
//if (!IsSqlEditionSupported(_options.ConnectionString))
33-
//{
34-
// throw new PlatformNotSupportedException(Resources.Error_UnsupportedSqlEdition);
35-
//}
36-
37-
var script = GetType().Assembly.StringResource("install.sql");
31+
string script;
32+
DbOperation operation;
33+
34+
if (_options.AutoEnableServiceBroker)
35+
{
36+
try
37+
{
38+
script = GetType().Assembly.StringResource("enable-broker.sql");
39+
operation = new DbOperation(_options.ConnectionString, script, _logger);
40+
operation.ExecuteNonQuery();
41+
}
42+
catch (Exception ex)
43+
{
44+
_logger.LogError(ex, "Unable to automatically enable SQL Server Service Broker.");
45+
}
46+
}
47+
48+
script = GetType().Assembly.StringResource("install.sql");
3849

3950
script = script.Replace("SET @SCHEMA_NAME = 'SignalR';", "SET @SCHEMA_NAME = '" + _options.SchemaName + "';");
40-
script = script.Replace("SET @SCHEMA_TABLE_NAME = 'Schema';", "SET @SCHEMA_TABLE_NAME = '" + SchemaTableName + "';");
4151
script = script.Replace("SET @TARGET_SCHEMA_VERSION = 1;", "SET @TARGET_SCHEMA_VERSION = " + SchemaVersion + ";");
4252
script = script.Replace("SET @MESSAGE_TABLE_COUNT = 1;", "SET @MESSAGE_TABLE_COUNT = " + _options.TableCount + ";");
4353
script = script.Replace("SET @MESSAGE_TABLE_NAME = 'Messages';", "SET @MESSAGE_TABLE_NAME = '" + _messagesTableNamePrefix + "';");
4454

45-
var operation = new DbOperation(_options.ConnectionString, script, _logger);
55+
operation = new DbOperation(_options.ConnectionString, script, _logger);
4656
operation.ExecuteNonQuery();
4757

4858
_logger.LogInformation("SignalR SQL objects installed");
4959
}
50-
51-
private bool IsSqlEditionSupported(string connectionString)
52-
{
53-
var operation = new DbOperation(connectionString, "SELECT SERVERPROPERTY ( 'EngineEdition' )", _logger);
54-
var edition = (int)operation.ExecuteScalar()!;
55-
56-
return (edition >= SqlEngineEdition.Standard && edition <= SqlEngineEdition.Express) ||
57-
edition == SqlEngineEdition.SqlAzureManagedInstance;
58-
}
59-
60-
private static class SqlEngineEdition
61-
{
62-
// See article http://technet.microsoft.com/en-us/library/ms174396.aspx for details on EngineEdition
63-
public const int Personal = 1;
64-
public const int Standard = 2;
65-
public const int Enterprise = 3;
66-
public const int Express = 4;
67-
public const int SqlAzure = 5;
68-
public const int SqlAzureManagedInstance = 8;
69-
}
7060
}
7161
}

src/IntelliTect.AspNetCore.SignalR.SqlServer/Internal/SqlServer/SqlReceiver.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ internal class SqlReceiver : IDisposable
2727
private string _selectSql = "SELECT [PayloadId], [Payload], [InsertedOn] FROM [{0}].[{1}] WHERE [PayloadId] > @PayloadId";
2828
private ObservableDbOperation? _dbOperation;
2929
private volatile bool _disposed;
30-
private CancellationTokenSource _receiveCancellation;
3130

3231
public SqlReceiver(SqlServerOptions options, ILogger logger, string tableName, string tracePrefix)
3332
{

src/IntelliTect.AspNetCore.SignalR.SqlServer/Internal/SqlServer/SqlStream.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ internal class SqlStream : IDisposable
2020
private readonly SqlReceiver _receiver;
2121
private readonly string _tracePrefix;
2222

23-
public SqlStream(SqlServerOptions options, ILogger logger, int streamIndex, string tableName)
23+
public SqlStream(SqlServerOptions options, ILogger logger, int streamIndex, string tableName, string tracePrefix)
2424
{
2525
_streamIndex = streamIndex;
2626
_logger = logger;
27-
_tracePrefix = String.Format(CultureInfo.InvariantCulture, "Stream {0} : ", _streamIndex);
27+
_tracePrefix = tracePrefix;
2828

2929
Queried += () => { };
3030
Received += (_, __) => { };

0 commit comments

Comments
 (0)