diff --git a/samples/ChatSample/ChatSample.csproj b/samples/ChatSample/ChatSample.csproj
index 8c6f0ddf1d..30024a8cc9 100644
--- a/samples/ChatSample/ChatSample.csproj
+++ b/samples/ChatSample/ChatSample.csproj
@@ -41,6 +41,7 @@
+
diff --git a/samples/ChatSample/RedisUserTracker.cs b/samples/ChatSample/RedisUserTracker.cs
index e70de95161..ec55b6bf58 100644
--- a/samples/ChatSample/RedisUserTracker.cs
+++ b/samples/ChatSample/RedisUserTracker.cs
@@ -13,18 +13,23 @@ using Microsoft.AspNetCore.SignalR.Redis;
using Microsoft.AspNetCore.Sockets;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
using StackExchange.Redis;
namespace ChatSample
{
public class RedisUserTracker : IUserTracker
{
- private readonly RedisKey UsersOnlineRedisKey = "UsersOnline";
+ private readonly RedisKey UserIndexRedisKey = "UserIndex";
private readonly int _redisDatabase;
private readonly ConnectionMultiplexer _redisConnection;
private readonly ISubscriber _redisSubscriber;
private readonly ILogger _logger;
- private readonly RedisChannel _redisChannel;
+
+ private const string UserAddedChannelName = "UserAdded";
+ private const string UserRemovedChannelName = "UserRemoved";
+ private readonly RedisChannel _userAddedChannel;
+ private readonly RedisChannel _userRemovedChannel;
public event Action UserJoined;
public event Action UserLeft;
@@ -35,21 +40,11 @@ namespace ChatSample
_redisDatabase = options.Value.Options.DefaultDatabase.GetValueOrDefault();
_redisConnection = ConnectToRedis(options.Value, _logger);
_redisSubscriber = _redisConnection.GetSubscriber();
- _redisChannel = new RedisChannel((string)UsersOnlineRedisKey, RedisChannel.PatternMode.Literal);
- _redisSubscriber.Subscribe(_redisChannel, (channel, value) =>
- {
- var stringValue = (string)value;
- var user = ToUserDetails(stringValue.Substring(1));
- if (stringValue[0] == '-')
- {
- UserLeft(user);
- }
- else
- {
- UserJoined(user);
- }
- });
+ _userAddedChannel = new RedisChannel(UserAddedChannelName, RedisChannel.PatternMode.Literal);
+ _userRemovedChannel = new RedisChannel(UserRemovedChannelName, RedisChannel.PatternMode.Literal);
+ _redisSubscriber.Subscribe(_userAddedChannel, (channel, value) => UserJoined(DeserializerUser(value)));
+ _redisSubscriber.Subscribe(_userRemovedChannel, (channel, value) => UserLeft(DeserializerUser(value)));
}
private static ConnectionMultiplexer ConnectToRedis(RedisOptions options, ILogger logger)
@@ -71,37 +66,48 @@ namespace ChatSample
return ConnectionMultiplexer.Connect(configurationOptions, loggerTextWriter);
}
- private static UserDetails ToUserDetails(string user)
- {
- var pos = user.IndexOf("|");
- Debug.Assert(pos >= 0, "Invalid user details format");
- return new UserDetails(user.Substring(0, pos), user.Substring(pos + 1));
- }
-
public async Task AddUser(Connection connection, UserDetails userDetails)
{
var database = _redisConnection.GetDatabase(_redisDatabase);
- var user = $"{connection.ConnectionId}|{connection.User.Identity.Name}";
-
+ var key = GetUserRedisKey(connection);
+ var user = SerializeUser(connection);
// need to await to make sure user is added before we call into the Hub
- await database.SetAddAsync(UsersOnlineRedisKey, $"{connection.ConnectionId}|{connection.User.Identity.Name}");
- _ = _redisSubscriber.PublishAsync(_redisChannel, "+" + user);
+ await database.StringSetAsync(key, SerializeUser(connection));
+ await database.SetAddAsync(UserIndexRedisKey, key);
+ _ = _redisSubscriber.PublishAsync(_userAddedChannel, user);
}
public async Task RemoveUser(Connection connection)
{
var database = _redisConnection.GetDatabase(_redisDatabase);
- var user = $"{connection.ConnectionId}|{connection.User.Identity.Name}";
-
- await database.SetRemoveAsync(UsersOnlineRedisKey, user);
- _ = _redisSubscriber.PublishAsync(_redisChannel, "-" + user);
+ await database.SetRemoveAsync(UserIndexRedisKey, connection.ConnectionId);
+ if (await database.KeyDeleteAsync(GetUserRedisKey(connection)))
+ {
+ _ = _redisSubscriber.PublishAsync(_userRemovedChannel, SerializeUser(connection));
+ }
}
public async Task> UsersOnline()
{
var database = _redisConnection.GetDatabase(_redisDatabase);
- var usersOnline = await database.SetMembersAsync(UsersOnlineRedisKey);
- return usersOnline.Select(u => ToUserDetails(u));
+ var userIds = await database.SetMembersAsync(UserIndexRedisKey);
+ var users = await database.StringGetAsync(userIds.Select(id => (RedisKey)(string)id).ToArray());
+ return users.Select(user => DeserializerUser(user));
+ }
+
+ private static string GetUserRedisKey(Connection connection)
+ {
+ return $"user:{connection.ConnectionId}";
+ }
+
+ private static string SerializeUser(Connection connection)
+ {
+ return $"{{ \"ConnectionID\": \"{connection.ConnectionId}\", \"Name\": \"{connection.User.Identity.Name}\" }}";
+ }
+
+ private static UserDetails DeserializerUser(string userJson)
+ {
+ return JsonConvert.DeserializeObject(userJson);
}
private class LoggerTextWriter : TextWriter
diff --git a/samples/ChatSample/Startup.cs b/samples/ChatSample/Startup.cs
index 00f2844077..86ff669993 100644
--- a/samples/ChatSample/Startup.cs
+++ b/samples/ChatSample/Startup.cs
@@ -56,20 +56,18 @@ namespace ChatSample
// To use Redis scaleout uncomment .AddRedis and uncomment Redis related lines below for presence
services.AddSignalR()
- // .AddRedis()
+ .AddRedis()
;
services.AddAuthentication();
- services.AddSingleton(typeof(DefaultHubLifetimeManager<>), typeof(DefaultHubLifetimeManager<>));
- services.AddSingleton(typeof(HubLifetimeManager<>), typeof(DefaultPresenceHublifetimeMenager<>));
- services.AddSingleton(typeof(IUserTracker<>), typeof(InMemoryUserTracker<>));
+ //services.AddSingleton(typeof(DefaultHubLifetimeManager<>), typeof(DefaultHubLifetimeManager<>));
+ //services.AddSingleton(typeof(HubLifetimeManager<>), typeof(DefaultPresenceHublifetimeMenager<>));
+ //services.AddSingleton(typeof(IUserTracker<>), typeof(InMemoryUserTracker<>));
- /*
services.AddSingleton(typeof(RedisHubLifetimeManager<>), typeof(RedisHubLifetimeManager<>));
services.AddSingleton(typeof(HubLifetimeManager<>), typeof(RedisPresenceHublifetimeMenager<>));
services.AddSingleton(typeof(IUserTracker<>), typeof(RedisUserTracker<>));
- */
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
diff --git a/samples/ChatSample/Views/Home/Index.cshtml b/samples/ChatSample/Views/Home/Index.cshtml
index aa2ce7c793..89b01f6800 100644
--- a/samples/ChatSample/Views/Home/Index.cshtml
+++ b/samples/ChatSample/Views/Home/Index.cshtml
@@ -19,7 +19,6 @@
let transportType = signalR.TransportType[getParameterByName('transport')] || signalR.TransportType.WebSockets;
let connection = new signalR.HubConnection(`http://${document.location.host}/chat`, 'formatType=json&format=text');
-// connection.on('Send', message => appendLine(message));
connection.onClosed = e => {
if (e) {
appendLine('Connection closed with error: ' + e, 'red');