Hi @ismcagdas, No problem on the delay, I know 7.2 was a big job and took a lot of your time so I didn't interrupt. This is still a work in progress but as soon as I have SignalR working I will be using it more and more in my dashboards (hence the impatience for the configurable dashboards!). Here is my angular service, formSubmission-signalr.service.ts:
import { Injectable, Injector, NgZone } from '@angular/core';
import { AppComponentBase } from '@shared/common/app-component-base';
import { HubConnection } from '@aspnet/signalr';
@Injectable()
export class FormSubmissionSignalrService extends AppComponentBase {
constructor(
injector: Injector,
public _zone: NgZone
) {
super(injector);
}
formSubmissionHub: HubConnection;
isFormSubmissionHubConnected = false;
init(): void {
this._zone.runOutsideAngular(() => {
abp.signalr.connect();
abp.signalr.startConnection(abp.appPath + 'signalr-formSubmission', connection => {
abp.event.trigger('app.formSubmission.connected');
this.isFormSubmissionHubConnected = true;
this.configureConnection(connection);
});
});
}
configureConnection(connection): void {
// Set the common hub
this.formSubmissionHub = connection;
// Reconnect if hub disconnects
connection.onclose(e => {
this.isFormSubmissionHubConnected = false;
if (e) {
abp.log.debug('Form submission hub connection closed with error: ' + e);
} else {
abp.log.debug('Form submission hub disconnected');
}
if (!abp.signalr.autoConnect) {
return;
}
setTimeout(() => {
connection.start().then(result => {
this.isFormSubmissionHubConnected = true;
});
}, 5000);
});
// Register to get notifications
//this.registerChatEvents(connection);
this.registerGetFormSubmission(connection);
}
registerGetFormSubmission(connection): void {
connection.on('getFormSubmission', message => {
abp.event.trigger('app.formSubmission.newSubmission', message);
});
}
sendMessage(messageData, callback): void {
if (!this.isFormSubmissionHubConnected) {
if (callback) {
callback();
}
abp.notify.warn(this.l('ChatIsNotConnectedWarning'));
return;
}
this.formSubmissionHub.invoke('sendMessage', messageData).then(result => {
if (result) {
abp.notify.warn(result);
}
if (callback) {
callback();
}
}).catch(error => {
abp.log.error(error);
if (callback) {
callback();
}
});
}
}
My understanding that once we hit the line:
await _formSubmissionHub.Clients.All.SendAsync("getFormSubmission", tenantId);
in my server the message should be sent to all clients and then be seen at this point in angular:
registerGetFormSubmission(connection): void {
connection.on('getFormSubmission', message => {
abp.event.trigger('app.formSubmission.newSubmission', message);
});
}
Am I missing something? I must admit that the plumbing for the communicator class in dotnet-core still has me scratching my head as to how the dependencies work.
Hi @ismcagdas, I know your up to your ears with 7.2 but when you get a moment afterwards, could you take a look at this. I have created my Hub (above) in Web.Core. I can call this from the angular project and trigger a send from the same server, everything is good. Following your chat code I have attemped to send a message to all clients as follows. I create a new communicator class in my Web.Core in the same folder (namespace) as my hub:
using Abp.Dependency;
using Abp.Runtime.Session;
using Castle.Core.Logging;
using Microsoft.AspNetCore.SignalR;
using Nuagecare.App.SignalR;
using System.Threading.Tasks;
namespace Nuagecare.Web.App.SignalR.FormSubmissionHub
{
public class SignalRFormSubmissionCommunicator : ISignalRFormSubmissionCommunicator, ITransientDependency
{
private readonly IHubContext<FormSubmissionHub> _formSubmissionHub;
public IAbpSession AbpSession { get; set; }
public ILogger Logger { get; set; }
public SignalRFormSubmissionCommunicator(IHubContext<FormSubmissionHub> formSubmissionHub)
{
_formSubmissionHub = formSubmissionHub;
AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
}
public async Task SendFormSubmission(int tenantId)
{
await _formSubmissionHub.Clients.All.SendAsync("getFormSubmission", tenantId);
}
}
}
I create the interface in my Core project. I inject my communicator into my app service:
private readonly ISignalRFormSubmissionCommunicator _signalRFormSubmissionCommunicator;
and make a simple call:
await _signalRFormSubmissionCommunicator.SendFormSubmission((int)AbpSession.TenantId);
The code hits my communicator method and everything looks fine but the call is not recieved in my angular project. Any simple pointers?
Hi @ismcagdas, Supposing I implemented a controller in the Web.Host project, call my app service to process one document at a time and then called the Hub in Web.Core with my message at the end of each iteration? Architecturally does this break any rules or can you think of any reason why this approach would not work?
Hi @ismcagdas, thanks for giving over your time. I'm still not sure how to create the code in my Core project and call it from my Hub when the Core project is not available from the Web.Core project (and vice-versa). So I will try and explain with some code. The use case is that each tenant has a set of default Word documents held in Azure blob storage. When a new entity (in my case, a resident in a care home) is added to the system the user selects a set of default documents for the resident. The list of documents is passed back to the server layer (NcDocumentAppService):
[AbpAuthorize]
public async Task<string> CreateDefaultDocuments(LoadDefaultDocumentsInput input)
{
var tenancyName = await GetTenancyNameFromTenantId();
var targetContainer = await _blobMethods.SetUpContainer(tenancyName);
_blobMethods.cloudBlobContainer = targetContainer;
var ncEntity = ObjectMapper.Map<NcEntityDto>(await _ncEntityRepository.FirstOrDefaultAsync(input.NcEntityId));
var sourceFolderName = "documentTemplates/ncEntities";
foreach (var documentTemplateId in input.NcDocumentTemplateIds)
{
var documentTemplate = ObjectMapper.Map<NcDocumentTemplateDto>(await _ncDocumentTemplateRepository.FirstOrDefaultAsync(documentTemplateId));
var SubFolder = documentTemplate.Uri;
var defaultDocumentTemplate = await DownloadDefaultDocument(sourceFolderName, documentTemplate.DisplayName);
var mergedDocument = MergeDocument(defaultDocumentTemplate, ncEntity, tenancyName);
var documentDefault = await _ncDocumentDefaultRepository.FirstOrDefaultAsync(m => m.Id == documentTemplate.NcDocumentDefaultId);
var targetFolderName = await GetFolderBreadcrumb(documentDefault.NcDocumentFolderId);
var metaData = LoadMetaData(GetContentType(documentTemplate.DisplayName), documentTemplate, targetFolderName, ncEntity.Id.ToString());
var blob = await LoadDocument(mergedDocument, metaData, documentTemplate.DisplayName, GetContentType(documentTemplate.DisplayName));
var newDocument = await NewCreateOrEditNcDocumentInput(documentTemplate.DisplayName, documentTemplate, ncEntity, blob.Uri.ToString());
await CreateOrEdit(newDocument);
// I WANT TO CALL NcDocumentHub.SendDocumentMergedMessage() HERE AND UPDATE THE UI TO SHOW THE DOCUMENT HAS BEEN LOADED
}
return "";
}
The service method gets the default document template from the database, downloads from blob storage, carries out a merge using known entity values and then uploads the merged document to blob storage and then persists a control record in the database. The process can take up to 10 seconds per document and there are 20-30 documents to prepare. Therefore I would like to use SignalR to update the UI with a messagefor each document which has been downloaded, merged, uploaded and persisted. Here is a simple Hub class to go along with it.
using Abp.Dependency;
using Abp.Runtime.Session;
using Castle.Core.Logging;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace Nuagecare.Web.App.SignalR.NcDocument
{
public class NcDocumentHub : Hub, ITransientDependency
{
public IAbpSession AbpSession { get; set; }
public ILogger Logger { get; set; }
public NcDocumentHub()
{
AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
}
public async Task SendDocumentMergedMessage(string documentName)
{
await Clients.All.SendAsync("getDocumentMergedMessage", string.Format("{0} merged and loaded.", documentName));
}
}
}
How do I hook this together? Sorry to be such a pain but I've been going round in circles for a couple of days.
HI @ismcagdas, sorry but I'm not sure exactly what you mean here. The code is a new hub and I want to call a method in the hub from my service (ProjectName.Application) project. What code, exactly, do I move to the ProjectName.Core project? The hub? If so which nuget packages should be added to ProjectName.Core to support abp's implementation of SignalR? Sorry, but it's not exactly clear from the documentation. I tried to follow your code for notifications but this involves abp dll's and, in the end, it was unfathomable for me.
Hi Guys, Following the guidelines at SignalR Integration I have created a new SignalR hub based on the ChatHub in the Web.Core project. I want to be able to call a method on the hub from a service in my Application project but the Web.Core project is not available. The Zero chathub is accessed from the ChatController in the Web.Host project but I do not have a controller, simply a service in the Application project. What's the best way to do this?
Done. User lockout
This is something I have also asked for. The simple ability to have a user in the host with access to certain tenants. It would be a great feature.
Currently a user can lock themselves out of the system and an admin user (or someone with the requisite permission) can unlock that person by using the drop down Admin -> Users. It would be nice to see users who are locked out on the user grid and also the number of failed logins. Should I raise this as a change request on github or is it not something in your plans? Just asking, it's not a show stopper.
@bbakermmc, I just found this in my junk folder, go figure... Thanks for this.