diff --git a/EJ2CoreSampleBrowser_NET10.csproj b/EJ2CoreSampleBrowser_NET10.csproj index 9bb178cd..b5ecd70e 100644 --- a/EJ2CoreSampleBrowser_NET10.csproj +++ b/EJ2CoreSampleBrowser_NET10.csproj @@ -47,6 +47,7 @@ + diff --git a/EJ2CoreSampleBrowser_NET8.csproj b/EJ2CoreSampleBrowser_NET8.csproj index e7fa87bf..3b0c19f9 100644 --- a/EJ2CoreSampleBrowser_NET8.csproj +++ b/EJ2CoreSampleBrowser_NET8.csproj @@ -47,6 +47,7 @@ + diff --git a/EJ2CoreSampleBrowser_NET9.csproj b/EJ2CoreSampleBrowser_NET9.csproj index 08779feb..26720efa 100644 --- a/EJ2CoreSampleBrowser_NET9.csproj +++ b/EJ2CoreSampleBrowser_NET9.csproj @@ -47,6 +47,7 @@ + diff --git a/Pages/DiagramHub.cs b/Pages/DiagramHub.cs index 68c72b48..7932da1c 100644 --- a/Pages/DiagramHub.cs +++ b/Pages/DiagramHub.cs @@ -10,6 +10,7 @@ using EJ2CoreSampleBrowser.Models; using System.Collections.Concurrent; using Newtonsoft.Json; +using AsyncKeyedLock; namespace EJ2SDiagramSample.Pages { @@ -117,64 +118,52 @@ public async Task SendCurrentSelectionsToCaller() } #endregion - private static readonly ConcurrentDictionary _connLocks = new(); + private static readonly AsyncKeyedLocker _connLocks = new(); - private static SemaphoreSlim GetConnectionLock(string connectionId) - { - return _connLocks.GetOrAdd(connectionId, _ => new SemaphoreSlim(1, 1)); - } public async Task BroadcastToOtherClients(List payloads, long clientVersion, List? elementIds, SelectionEvent currentSelection, string roomName) { var connId = Context.ConnectionId; - var gate = GetConnectionLock(connId); - await gate.WaitAsync(); - try - { - var versionKey = "diagram:version"; + using var _ = await _connLocks.LockAsync(connId); + var versionKey = "diagram:version"; - var (acceptedSingle, serverVersionSingle) = await _redisService.CompareAndIncrementAsync(versionKey, clientVersion); - long serverVersionFinal = serverVersionSingle; + var (acceptedSingle, serverVersionSingle) = await _redisService.CompareAndIncrementAsync(versionKey, clientVersion); + long serverVersionFinal = serverVersionSingle; - if (!acceptedSingle) + if (!acceptedSingle) + { + var recentUpdates = await GetUpdatesSinceVersionAsync(clientVersion, maxScan: 200); + var recentlyTouched = new HashSet(StringComparer.Ordinal); + foreach (var upd in recentUpdates) { - var recentUpdates = await GetUpdatesSinceVersionAsync(clientVersion, maxScan: 200); - var recentlyTouched = new HashSet(StringComparer.Ordinal); - foreach (var upd in recentUpdates) - { - if (upd.ModifiedElementIds == null) continue; - foreach (var id in upd.ModifiedElementIds) - recentlyTouched.Add(id); - } - - var overlaps = elementIds?.Where(id => recentlyTouched.Contains(id)).Distinct().ToList(); - if (overlaps?.Count > 0) - { - await Clients.Caller.SendAsync("RevertCurrentChanges", elementIds); - await Clients.Caller.SendAsync("ShowConflict"); - return; - } - - var (_, newServerVersion) = await _redisService.CompareAndIncrementAsync(versionKey, serverVersionSingle); - serverVersionFinal = newServerVersion; + if (upd.ModifiedElementIds == null) continue; + foreach (var id in upd.ModifiedElementIds) + recentlyTouched.Add(id); } - var update = new DiagramUpdateMessage + var overlaps = elementIds?.Where(id => recentlyTouched.Contains(id)).Distinct().ToList(); + if (overlaps?.Count > 0) { - SourceConnectionId = connId, - Version = serverVersionFinal, - ModifiedElementIds = elementIds - }; + await Clients.Caller.SendAsync("RevertCurrentChanges", elementIds); + await Clients.Caller.SendAsync("ShowConflict"); + return; + } - await StoreUpdateInRedis(update, connId); - SelectionEvent selectionEvent = BuildSelectedElementEvent(currentSelection.ElementIds, currentSelection.SelectorBounds); - await UpdateSelectionBoundsInRedis(selectionEvent, currentSelection.ElementIds, currentSelection.SelectorBounds); - await Clients.OthersInGroup(roomName).SendAsync("ReceiveData", payloads, serverVersionFinal, selectionEvent); - await RemoveOldUpdates(serverVersionFinal); + var (_, newServerVersion) = await _redisService.CompareAndIncrementAsync(versionKey, serverVersionSingle); + serverVersionFinal = newServerVersion; } - finally + + var update = new DiagramUpdateMessage { - gate.Release(); - } + SourceConnectionId = connId, + Version = serverVersionFinal, + ModifiedElementIds = elementIds + }; + + await StoreUpdateInRedis(update, connId); + SelectionEvent selectionEvent = BuildSelectedElementEvent(currentSelection.ElementIds, currentSelection.SelectorBounds); + await UpdateSelectionBoundsInRedis(selectionEvent, currentSelection.ElementIds, currentSelection.SelectorBounds); + await Clients.OthersInGroup(roomName).SendAsync("ReceiveData", payloads, serverVersionFinal, selectionEvent); + await RemoveOldUpdates(serverVersionFinal); } private async Task RemoveOldUpdates(long currentServerVersion) { @@ -379,7 +368,7 @@ public async Task JoinDiagram(string roomName, string diagramId, string userName _diagramUsers.AddOrUpdate(userId, diagramUser, (key, existingValue) => diagramUser ); - await RequestAndLoadStateAsync(roomName,diagramId,Context.ConnectionId,Context.ConnectionAborted); + await RequestAndLoadStateAsync(roomName, diagramId, Context.ConnectionId, Context.ConnectionAborted); long currentServerVersion = await GetDiagramVersion(); await Clients.Caller.SendAsync("UpdateVersion", currentServerVersion);