Perfect, done :)
Using background tasks with hosted services in ASP.NET Core it works well. https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0&tabs=visual-studio
Beautiful repo explaining all this -> https://github.com/Daniel-Krzyczkowski/AzureDeveloperTemplates/tree/34394d365a72c0325a5e155d7bc0b4547f8ded9e
if anyone needs.
Sorry, me again. From a consumer point of view, do you think this is a good idea? https://damienbod.com/2019/04/23/using-azure-service-bus-queues-with-asp-net-core-services/
Using service bus queues (or event hub) to consume and then make use of AppNotifier (and mobile app push) from within web.host.
Hey,
Thanks so much for the kind offer. We have decided to rather push the telemetry messages to the web host using Azure Service Bus and then from there out to all the users via AppNotifier. We are very familiar with Service Bus it and it will streamline better with ABP imo. We also wont need Azure Signal R either.
Going to close this question. Thanks all.
Great stuff, thanks. I will get going and prepare a solution for you. You will need access to an Azure account regarding an Azure Signal R resource so either if you have a dev one to use yours or we can expose ours all already done in the solution.
In fact the entire process will be easier if you just had full access. Let me get that for you thanks!
Hey @ismcagdas
I will demo the process using appNotifier.SendMessageAsync
I have an Azure function which gets triggered by a cosmosDB changefeed (on insert). This is where I would like to push to the relevent user with information about the relevent IoT Message. The problem comes how to setup Signal R/Azure Signal R within an Azure function and even more so, in order to reuse existing logic and abp modules etc etc.
As it stands registering Signal R like this wont work based on the Azure function design:
builder.Services
.AddSignalR(options => { options.EnableDetailedErrors = true; })
.AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
{
new ServiceEndpoint(_appConfiguration["Azure:SignalR:PrimaryConnectionString"], EndpointType.Primary, "Name1"),
new ServiceEndpoint(_appConfiguration["Azure:SignalR:SecondaryConnectionString"], EndpointType.Secondary, "Name2"),
});
Azure Function : Microsoft.Azure.WebJobs.Extensions.SignalRService is the package that I attempted with [SignalRConnectionInfo(HubName = "chathub")] SignalRConnectionInfo connectionInfo - https://github.com/Azure/azure-functions-signalrservice-extension
namespace SynapWare.AzureFunction.IoT
{
public class CosmosdbChangeFeed
{
private readonly LatestDeviceDataRepository _latestdeviceDataRepository;
public CosmosdbChangeFeed(ICosmosDbClientFactory latestdeviceDataRepository)
{
_latestdeviceDataRepository = new LatestDeviceDataRepository(latestdeviceDataRepository);
}
[FunctionName("CosmosdbChangeFeed")]
public async Task Run([CosmosDBTrigger(
databaseName: "synap-staging-db",
collectionName: "devicedata",
ConnectionStringSetting = "ConnectionString",
LeaseCollectionName = "lease-latestdevicedata",
CreateLeaseCollectionIfNotExists = true)]IReadOnlyList<Document> input, ILogger log,
//SignalR output Binding
//[SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
[SignalRConnectionInfo(HubName = "chathub")] SignalRConnectionInfo connectionInfo)
{
if (input != null && input.Count > 0)
{
//Build connection with token
HubConnection _connection = new HubConnectionBuilder()
.WithUrl(connectionInfo.Url, option =>
{
option.Headers.Add("Authorization", $"Bearer {connectionInfo.AccessToken}");
})
.Build();
//Start the hub connection
await _connection.StartAsync();
//My Hub has a BroadcastMessage method that receives 2 arguments
//await _connection.SendAsync("getChatMessage", "test", "test", default);
//ChatMessageDto tst = new ChatMessageDto()
//{
// TenantId = null,
// UserId = 1,
// Message = "test",
// CreationTime = DateTime.UtcNow,
// TargetUserId = 5,
// TargetTenantId = null
//};
//await _connection.SendAsync("getChatMessage", tst);
await _connection.SendAsync("App.SimpleMessage", "test");
foreach (var document in input)
{
//TODO : Only use the latest event for each device
var jsonSer = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() }
};
var deviceData = JsonConvert.DeserializeObject<DeviceData>(document.ToString(), jsonSer);
deviceData.Id = deviceData.Serial;
await _latestdeviceDataRepository.UpsertAsync(deviceData);
// broadcast through SignalR
//var signalR = new AzureSignalR("Endpoint=https://synap-staging-signalr- service.service.signalr.net;AccessKey=abc=;Version=1.0;");
//await signalR.SendAsync("chathub", "updateSignInStats", "hello world");
//await signalRMessages.AddAsync(new SignalRMessage
//{
// Target = "getChatMessage",//"newMessage",
// Arguments = new[] { deviceData }
//});
using (var obj = Startup.iocManager.ResolveAsDisposable<AzFunctionAbpExecuter>())
{
//await obj.Object.AddNewDeviceEventAppNotifier(deviceData);
await obj.Object.SendTestMessageAppNotifier();
}
}
}
}
}
}
The function above updates a cosmosdb collection and then I would love to push telemetry messages to the UI.
The ABP module for the Azure Function is defined as
namespace SynapWare.AzureFunction.IoT
{
[DependsOn
(typeof(AbpZeroCoreEntityFrameworkCoreModule),
typeof(SynapWareEntityFrameworkCoreModule),
typeof(AbpAspNetZeroCoreModule),
typeof(SynapWareCoreModule),
typeof(SynapWareClientModule),
typeof(AbpAutoMapperModule),
typeof(AbpZeroCoreModule),
typeof(AbpAspNetCoreSignalRModule))]
public class SynapWareAzFuncIoTModule : AbpModule
{
private readonly IConfigurationRoot _appConfiguration;
private readonly IServiceCollection _serviceCollection;
public SynapWareAzFuncIoTModule()
{
_appConfiguration = Startup._appConfiguration;
_serviceCollection = Startup.serviceCollection;
}
public override void PreInitialize()
{
Configuration.Localization.IsEnabled = false;
Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
Configuration.Modules.AspNetZero().LicenseCode = _appConfiguration["AbpZeroLicenseCode"];
//set time zone
Clock.Provider = ClockProviders.Utc;
Configuration.DefaultNameOrConnectionString = _appConfiguration.GetConnectionString(
SynapWareConsts.ConnectionStringName
);
Configuration.ReplaceService(typeof(IEventBus), () =>
{
IocManager.IocContainer.Register(
Component.For<IEventBus>().Instance(NullEventBus.Instance)
);
});
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(SynapWareAzFuncIoTModule).GetAssembly());
Register(IocManager);
}
public static void Register(IIocManager iocManager)
{
var services = new ServiceCollection();
IdentityRegistrar.Register(services);
WindsorRegistrationHelper.CreateServiceProvider(iocManager.IocContainer, services);
}
}
}
And finally the AbpExecutor
namespace SynapWare.AzureFunction.IoT
{
public class AzFunctionAbpExecuter : ITransientDependency
{
private readonly IRepository<Tenant> _tenantRepository;
private readonly UserManager _userManager;
private readonly IAppNotifier _appNotifier;
public AzFunctionAbpExecuter(IRepository<Tenant> tenantRepository, IAppNotifier appNotifier, UserManager userManager)
{
_tenantRepository = tenantRepository;
_appNotifier = appNotifier;
_userManager = userManager;
}
public async Task AddNewDeviceEventAppNotifier(SynapWare.CosmosDB.DeviceData data)
{
await _appNotifier.NewDeviceEventAsync(data);
}
public async Task SendTestMessageAppNotifier()
{
var user = await _userManager.FindByEmailAsync("[email protected]");
await _appNotifier.SendMessageAsync(
user.ToUserIdentifier(), "test", Abp.Notifications.NotificationSeverity.Warn);
}
}
}
The UI updates (with the relevenet db changes) when using await _appNotifier.SendMessageAsyn for example but nothing real time (as signal r not setup or pushed correctly)
Goodluck for that and thanks for responding guys :)
Yeah Im still puzzled about how to best push messages into Azure Signal R from outside the web host whilst still using as much of the abp/anz framework/work as possible. I can do it manually but would love to stream line it as best as possible - such as using AppNotifier?
BTW on default mode it now works with web.host. It might have been me flipping too often and breaking Azure :)
If you dont mind explaining your Azure Signal R setup - Settings and how you are pushing messages outside the web.host that would be greatly appreciated. I want to try utilize the core libs (abp framework in general) as much as possible without bringing in too much of my own code hence why pushing on with this.
Hey @rickfrankel
Thanks for the awesome info!
So what you said got me digging into the service mode. What is yours? Default, serverless, classic?
In Serverless: Azure SignalR Service is not connected yet, please try again later.
In Default Connection count reaches limit.
However, surely Serverless is the correct approach if I plan on utilizing an Azure Function to push messages (IoT device telemetry messages) as well, outside of the web host?
...Im now starting to think not, 2 seperate Azure Signal R resources. One default for abp and a serverless for azure function?
Its working! However, needs to be in classic mode. Azure functions need to be set in serverless mode. Trigger off say a cosmosdb insert, would love to utilize appnotifier to push deviceeventdata through the system.