Having received no response to the posts and emails sent, I open a dedicated ticket.
See my post https://abp.io/qa/questions/3052/3a18165e-11ab-1b69-892b-659fe71558de
Using Blazor Wasm (abp 8.3.2) I need to implement the real-time notification system already present in AspNetZero
I already read https://abp.io/docs/latest/framework/real-time/signalr, but it is not specific to Blazor WASM
I already read https://learn.microsoft.com/en-us/aspnet/core/blazor/tutorials/signalr-blazor?view=aspnetcore-8.0&tabs=visual-studio
and I tried to implement it but I have problems both with the Hub management and with the use of reusable Blazor components inside the Lepton-x toolbar (https://abp.io/support/questions/8800/Double-Initialization-of-Custom-Toolbar-Component)
Can you give me some information on when this feature will be available?
Or can you guide me step by step to implement this service?
Thanks
Roberto
12 Answer(s)
-
0
Can you give me some information on when this feature will be available?
It seems a nice & fancy feature so far, but whenever I checked the roadmap of the ABP team, I can't see this feature soon in the planned milestones. However you can still implement it on your own.
I already read https://abp.io/docs/latest/framework/real-time/signalr, but it is not specific to Blazor WASM
ABP framework doesn't provide a real-time notification system, it's an application logic your project needs. And it can be different depending on the case. There are several approaches you can consider:
-
Web Push Notifications: This approach uses the browser's Push API to deliver notifications even when the application is not active. It's great for engaging users who aren't actively using your app and works across different browsers and devices. However, it requires user permission and may not be supported in all browsers.
-
SignalR/WebSocket: This real-time communication approach establishes a persistent connection between client and server, enabling instant message delivery. It's ideal for real-time notifications as it provides immediate updates with minimal latency. The main advantages are bi-directional communication and efficient server resource usage. However, it may face challenges with firewalls, proxy servers, and requires maintaining open connections.
-
Polling Mechanism: This traditional approach involves the client periodically requesting updates from the server (e.g., every 30 seconds). It's simple to implement and works reliably across all environments. The downside is that it can be resource-intensive due to frequent server requests, may miss real-time updates between polls, and can impact server performance with many concurrent users.
Given your utilization of Blazor WebAssembly within the .NET ecosystem, we recommend implementing SignalR as the optimal solution for real-time communication. This robust technology provides a seamless and efficient approach to WebSocket implementation with minimal complexity.
and I tried to implement it but I have problems both with the Hub management and with the use of reusable Blazor components inside the Lepton-x toolbar (https://abp.io/support/questions/8800/Double-Initialization-of-Custom-Toolbar-Component)
Sorry to see this, but it seems it's not a problem currently. There are 2 instances of a single component, and you can handle it easily by using another service that has single instance and inject it to the components. That service can be a singleton or a scoped service. (If you run your blazor app Blazor Server, it has to be scoped instead of singleton). That service can manage the hub connection and the notifications without dependency on a UI component.
-
-
0
Thanks for reply,
if it was simple I would have already implemented it myself and I would not have opened the ticket nor insisted on requesting it as a feature request :-)
It's an application logic that many web portals need, I hope it will be planned as soon as possible.
It would be nice if the roadmap also included features for web applications and not just for microservices.Initially I would like to make something like this
https://abp.io/support/questions/7286/How-to-add-notification-UI-in-angular-with-bell-icon-and-count
and then extend it as a more complete service.In ABP documentation I found only these two documents
https://abp.io/docs/latest/framework/ui/blazor/toolbars?#example-add-a-notification-iconhttps://abp.io/community/articles/realtime-notifications-via-signalr-in-abp-project-tsp2uqd3
Can you provide me with a working example as ABP module (ABP version 8.3.2, Blazor WASM, EFCore) ?
Thanks
-
0
I'll try to provide a really qucik example:
-
Let say you already have something similar at your server-side:
[Authorize] public class NotificationHub : Hub { public async Task SendNotification(string message) { await Clients.All.SendAsync("ReceiveNotification", message); } }
Make sure you have
Volo.Abp.AspNetCore.SignalR
package installed on your host (backend) project<PackageReference Include="Volo.Abp.AspNetCore.SignalR" Version="9.0.4" />
[DependsOn(typeof(AbpAspNetCoreSignalRModule))]
_I assume the following configurations are done:_ ```csharp context.Services.AddSignalR(); ``` ```csharp app.UseConfiguredEndpoints(endpoints => { endpoints.MapHub("/signalr-hubs/notification"); }); ```
-
Now to to
Blazor.Client
project and make sure the following packages is installed and configured like below:
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.0" />
Implementing a shared service that handles events
Now you're ready to implement a shared single instanced service that can be consumed from 2 different instances of notification component.
NotificationService
public class NotificationService : ISingletonDependency, IAsyncDisposable { private HubConnection _hubConnection; private readonly ILogger<NotificationService> _logger; private bool _isConnected; // Event that components can subscribe to public event Action<string> OnNotificationReceived; public NotificationService( ILogger<NotificationService> logger) { _logger = logger; _logger.LogInformation("NotificationService Created!"); } public async Task InitializeAsync() { await InitializeHubConnection(); } private async Task InitializeHubConnection() { if (_hubConnection != null) { return; // Already initialized, return immediately } try { // Build the hub URL var hubUrl = "https://localhost:44343".TrimEnd('/') + "/signalr-hubs/notification"; _hubConnection = new HubConnectionBuilder() .WithUrl(hubUrl, options => { // Add token if needed // options.AccessTokenProvider = () => Task.FromResult(_tokenProvider.GetAccessToken()); }) .WithAutomaticReconnect() .Build(); // Register for the ReceiveNotification event _hubConnection.On<string>("ReceiveNotification", (message) => { _logger.LogInformation($"Notification received: {message}"); OnNotificationReceived?.Invoke(message); }); await _hubConnection.StartAsync(); _isConnected = true; _logger.LogInformation("SignalR connection established"); } catch (Exception ex) { _logger.LogError($"Error connecting to SignalR hub: {ex.Message}"); _isConnected = false; } } public async ValueTask DisposeAsync() { if (_hubConnection != null) { await _hubConnection.DisposeAsync(); } } }
NotificationComponent.razor
@namespace AbpSolution40.Blazor.Client.Components.Notification @using AbpSolution40.Blazor.Client.Services @using Microsoft.Extensions.Logging @using System.Collections.Generic @implements IDisposable @inject NotificationService NotificationService @inject ILogger<NotificationComponent> Logger <Dropdown> <DropdownToggle Color="Color.Primary"> <button type="button" class="btn btn-outline-primary position-relative"> <i class="fa fa-bell"></i> @if (Notifications.Count > 0) { <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger"> @Notifications.Count <span class="visually-hidden">unread notifications</span> </span> } </button> </DropdownToggle> <DropdownMenu> @foreach (var notification in Notifications) { <DropdownItem> <span>@notification.Message</span> <small class="notification-time">@notification.Timestamp.ToString("g")</small> </DropdownItem> } </DropdownMenu> </Dropdown> @code { private List<NotificationItem> Notifications { get; set; } = new List<NotificationItem>(); protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); NotificationService.OnNotificationReceived += HandleNotificationReceived; await NotificationService.InitializeAsync(); Logger.LogInformation("NotificationComponent initialized"); } private void HandleNotificationReceived(string message) { var notification = new NotificationItem { Id = Guid.NewGuid(), Message = message, Timestamp = DateTime.Now }; Notifications.Add(notification); StateHasChanged(); } public void Dispose() { NotificationService.OnNotificationReceived -= HandleNotificationReceived; } public class NotificationItem { public Guid Id { get; set; } public string Message { get; set; } = string.Empty; public DateTime Timestamp { get; set; } } } Id = Guid.NewGuid(), Message = message, Timestamp = DateTime.Now }; Notifications.Add(notification); StateHasChanged(); } private void ToggleNotificationsPopup() { _isPopupVisible = !_isPopupVisible; } private void RemoveNotification(NotificationItem notification) { Notifications.Remove(notification); StateHasChanged(); } public void Dispose() { NotificationService.OnNotificationReceived -= HandleNotificationReceived; } public class NotificationItem { public Guid Id { get; set; } public string Message { get; set; } = string.Empty; public DateTime Timestamp { get; set; } } }
And of course I configured this component in my newly created Toolbar Contributor:
public class AbpSolution40ToolbarContributor : IToolbarContributor { public Task ConfigureToolbarAsync(IToolbarConfigurationContext context) { if (context.Toolbar.Name == StandardToolbars.Main) { context.Toolbar.Items.Add(new ToolbarItem(typeof(NotificationComponent))); } return Task.CompletedTask; } }
And configured it in the
...ClientModule.cs
Configure<AbpToolbarOptions>(options => { options.Contributors.Add(new AbpSolution40ToolbarContributor()); });
It doesn't matter how many time the component is initialized. The
NotificationService
in the client-side is Singleton and it doesn't matter how many time it's resolved. There will be always single hub connection that listens the changes and delivers changes into component by using a C# Event namedOnNotificationReceived
. -
-
0
I try to create a solution and implement this code, but what should I put to set the correct url based on the environment?
// Build the hub URL var hubUrl = "***https://localhost:44343***".TrimEnd('/') + "/signalr-hubs/notification";
And to handle the token correctly, do I just uncomment the "options.AccessTokenProvider" line?
_hubConnection = new HubConnectionBuilder() .WithUrl(hubUrl, options => { // Add token if needed // options.AccessTokenProvider = () => Task.FromResult(_tokenProvider.GetAccessToken()); }) .WithAutomaticReconnect() .Build();
Thanks
-
0
This was just for demonstration but you can get it from the configuration by using
RemoteServiceConfigurationProvider
Inject it and use it and use
GetConfigurationOrDefaultAsync
method on the provider like this:var remoteServiceConfig = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("Default");
-
0
I need a real working example.
I don't know how to implement those two highlighted points in the correct way
Thanks
-
0
Hi,
It's a full-featured development that depened on some business deicions. Please start building on your own and ask the problems you faced during the development, then we can help on a specic case
-
0
Ok, to do it myself I would need links to documentation for these two points.
-
Set the correct url based on the environment in Abp solution
-
Set AccessTokenProvider in Abp solution
Can you give me the links?
Thanks
-
-
0
Hi @enisn,
I can't find the information requested.
Can you send me links to the documentation?Thanks!
-
0
It would be nice to have an answer and not wait more days.
If necessary, I open a new ticket. -
0
Hi
I sincerely apologize for the delay in my response and appreciate your patience.
In regard to your request for documentation on the two points you highlighted, please find the details below:
-
Set the correct url based on the environment in Abp solution
You can refer to the documentation on remote service configurations through the following link: https://abp.io/docs/latest/framework/api-development/dynamic-csharp-clients#configuration
-
Set AccessTokenProvider in Abp solution
For applications using WebAssembly (WASM), you can resolve the service mentioned in the link below to retrieve your access token:
https://github.com/abpframework/abp/blob/0b6ae95866a884f054f1684cea0eba62082b3e46/framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator.cs#L10 -
-
0
ok, thanks for the links.
Now I will study and try to put them into practice