Azure SignalR binding for WebJob .Net Core 3 not working - .net-core-3.0

I have a .Net Core 3.0 console project which includes WebJob function that has an output binding to Azure Signal R. The app builds OK, but when I run it and try to send a message out via SignalR I get the following error:
{System.MissingMethodException: Method not found: 'System.String Microsoft.Azure.SignalR.AuthenticationHelper.GenerateAccessToken(System.String, System.String, System.Collections.Generic.IEnumerable`1<System.Security.Claims.Claim>, System.TimeSpan, System.String)'.
at Microsoft.Azure.SignalR.Management.RestApiAccessTokenGenerator.Generate(String audience, Nullable`1 lifetime)
at Microsoft.Azure.SignalR.Management.RestApiProvider.GenerateRestApiEndpoint(String path, Nullable`1 lifetime)
at Microsoft.Azure.SignalR.Management.RestApiProvider.GetSendToGroupEndpoint(String groupName, Nullable`1 lifetime)
at Microsoft.Azure.SignalR.Management.RestHubLifetimeManager.SendGroupAsync(String groupName, String methodName, Object[] args, CancellationToken cancellationToken)
at Microsoft.AspNetCore.SignalR.Internal.GroupProxy`1.SendCoreAsync(String method, Object[] args, CancellationToken cancellationToken)
at Microsoft.Azure.WebJobs.Extensions.SignalRService.AzureSignalRClient.SendToGroup(String groupName, SignalRData data)
at Microsoft.Azure.WebJobs.Extensions.SignalRService.SignalRAsyncCollector`1.AddAsync(T item, CancellationToken cancellationToken)
at InSysWebJobP300DataProcessor.Helper.ProcessMinuteData(Message message, ConnectionMultiplexer redisConnection, IAsyncCollector`1 signalRMessages, ILogger log) in E:\InergySystems\GitHub\InSysCore\InSysWebJobP300DataProcessor\Helper.cs:line 125}
The SignalR service is registered in Program.Main with:
static void Main(string[] args)
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddServiceBus().AddSignalR();
});
builder.ConfigureLogging((context, b) =>
{
b.ClearProviders();
b.AddConfiguration(context.Configuration.GetSection("Logging"));
if (context.HostingEnvironment.IsDevelopment())
{
b.AddConsole();
}
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
I have a function with the following signature:
[FunctionName("ProcessMinuteData")]
public async Task RunAsync([ServiceBusTrigger("data", Connection = "AzureWebJobsServiceBusConnection")]Message message, [SignalR(HubName = "insyshub")] IAsyncCollector<SignalRMessage> signalRMessages, ILogger log)
The messages are received from the service bus are processed fine, but trying to push out a message via SignalR using the following, results in the error above:
await signalRMessages.AddAsync(new SignalRMessage
{
GroupName = "GroupName",
Target = "targetMethod",
Arguments = new[] { JsonConvert.SerializeObject(message) }
});
Note that the app does not need to receive messages via SignalR, just push them out.
I have the following NuGet packages installed:
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.Azure.SignalR" Version="1.2.1" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.14" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="3.0.5" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Http" Version="3.0.2" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="4.0.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.SignalRService" Version="1.0.2" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.10" />
<PackageReference Include="Microsoft.Azure.WebJobs.Logging.ApplicationInsights" Version="3.0.14" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.1" />

It seems that since Microsoft.Azure.SignalR 1.2.0 you now need to include Microsoft.Azure.SignalR.Management if you want to use the approach I am using detailed in the question.
Before 1.2.0 you didn't need to include Microsoft.Azure.SignalR.Management.

Related

Azure Functions and AppInsights RequestTelemetry

I'm trying to enhance RequestTelemetry in AppInsights from HttpTrigger Azure Function v3.
Function is initialized with DI and Startup class.
[assembly: FunctionsStartup(typeof(Startup))]
namespace Hager.Example.FunctionApp.FunctionApp
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// No service for repro
}
}
}
And my Function
public class Function1
{
private readonly ILogger _logger;
public Function1(ILogger<Function1> logger)
{
_logger = logger;
}
[FunctionName("HttpTriggered")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req)
{
using var loggerScope = _logger.BeginScope("{InScope1}{InScope2}{InScope3}", Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid());
_logger.LogInformation("Started Execution");
_logger.LogWarning("With a custom property: {CustomProperty}.", Guid.NewGuid());
Activity.Current?.AddTag("TagStart", Guid.NewGuid());
if (Activity.Current == null)
{
// Always null
_logger.LogError("No ActivityCurrent {Activity}.", Activity.Current);
_logger.LogError("ActivityCurrent Tags {Activity}.", Activity.Current?.Tags);
}
// Activity.Current.AddTag("Tag2", Guid.NewGuid()); // <- NullException
_logger.LogInformation("Finished Execution");
return new NoContentResult();
}
}
My project packages:
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.17.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.17.0" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.11" />
</ItemGroup>
Every logger and scopes are working as expected, but object ActivityTag is always null that shouldnt in Azure Functions.
Did I miss something?
Update:
Added Op's solution: by using request.HttpContext?.Features.Get<RequestTelemetry>(), it worked fine.
Please uninstall the 2 Application Insights packages: Microsoft.ApplicationInsights and Microsoft.ApplicationInsights.AspNetCore.
By default, Application Insights packages do not collect activity tags. So this should be the reason.
I tested your azure function without installing the above 2 Application Insights packages, it works well. Here is the screenshot of the test result:
Adding my local.settings.json here for your reference, the code is the same as yours:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "xxxx",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"APPINSIGHTS_INSTRUMENTATIONKEY": "xxx"
}
}
And if the 2 packages are necessary, maybe you can try add a custom ITelemetryInitializer by following this answer(btw, I didn't test it).

SignalR .Net Client with MessagePack - AddMessagePackProtocol Method Unrecognised in IHubConnectionBuilder

New user, trying to learn SignalR and Blazor Server, hoping somebody can help with this query. Struggling with getting the SignalR .NET Client to use MessagePack protocol in the Blazor Server Page.
.csproj Packages Installed
<ItemGroup>
<PackageReference Include="Autofac" Version="5.2.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
<!-- <PackageReference Include="MessagePack" Version="1.9.3" /> -->
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.7" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="3.1.7" />
</ItemGroup>
Originally I had installed 3.1.8 of SingalR Client and MessagePack packages. However, I have also tried downgrading to 3.1.7 and the issue still occurs.
This segment of code:
hubConnection = new HubConnectionBuilder()
.WithUrl(hubUrl)
.AddMessagePackProtocol()
.Build();
causes a build error:
error CS1061: 'IHubConnectionBuilder' does not contain a definition for 'AddMessagePackProtocol' and no accessible extension method 'AddMessagePackProtocol' accepting a first argument of type 'IHubConnectionBuilder' could be found (are you missing a using directive or an assembly reference?).....
Can anybody help? Am I missing an assembly #using reference?
Blazor Server Page
#page "/"
#using System.Threading;
#using System.Collections.Generic;
#using Microsoft.AspNetCore.SignalR.Client;
#using WebApp.Data;
#inject NavigationManager NavigationManager
<h1>Blazor Server App</h1>
<div>Latest message is => #_latestMessage</div>
<div id="scrollbox">
#foreach (var item in _messages)
{
<div>
<div>#item</div>
</div>
}
<hr />
</div>
#code {
private HubConnection hubConnection;
private string _latestMessage = "";
private List<string> _messages = new List<string>();
public bool IsConnected => hubConnection.State == HubConnectionState.Connected;
protected override async Task OnInitializedAsync()
{
var hubUrl = NavigationManager.BaseUri.TrimEnd('/') + "/motionhub";
// Uri uri = NavigationManager.ToAbsoluteUri("/motionhub");
try
{
hubConnection = new HubConnectionBuilder()
.WithUrl(hubUrl)
.AddMessagePackProtocol()
.Build();
hubConnection.On<string>("SendMotionDetection", ReceiveMessage);
await hubConnection.StartAsync();
Console.WriteLine("Index Razor Page initialised, listening on signalR hub url => " + hubUrl.ToString());
Console.WriteLine("Hub Connected => " + IsConnected);
}
catch (Exception e)
{
Console.WriteLine("Encountered exception => " + e);
}
}
private void ReceiveMessage(string message)
{
try
{
Console.WriteLine("Hey! I received a message");
_latestMessage = message;
_messages.Add(_latestMessage);
StateHasChanged();
}
catch (Exception ex)
{
Console.Error.WriteLine("An exception was encountered => " + ex.ToString());
}
}
}
Finally, got this to compile by adding:
#using Microsoft.Extensions.DependencyInjection;
Hope this saves some time for other new users that experience similar issue.
Just to clarify the correct answer already given by #anon_dc3spp it should be noted that you:
Need to have the Microsoft.AspNetCore.SignalR.Protocols.MessagePack Nuget Package installed on both the client and server.
Then you would use his reference shown below on the intended razor page... or maybe add it to your imports page:
#using Microsoft.Extensions.DependencyInjection

How to setup DI to inject ILogger<T> within a lambda function app with single lambda FunctionHandler

I have an MRE lambda project with csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AWSProjectType>Lambda</AWSProjectType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="1.1.0" />
<PackageReference Include="Amazon.Lambda.Logging.AspNetCore" Version="3.0.1" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.6" />
</ItemGroup>
</Project>
And I have created a simple functionhandler which calls _svc.DoSomethingThatLogsUsingLogger();
as shown below
using Amazon.Lambda.Core;
using AWSLambda3.Services;
using Microsoft.Extensions.DependencyInjection;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace AWSLambda3
{
public class Function
{
private IServiceName1 _svc { get; }
public Function( IServiceName1 svc)
{
_svc = svc;
}
public Function()
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
serviceCollection.AddLogging();
var serviceProvider = serviceCollection.BuildServiceProvider();
_svc = serviceProvider.GetService<IServiceName1>();
}
private void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<IServiceName1, ServiceName1>();
}
public string FunctionHandler(string input, ILambdaContext context)
{
_svc.DoSomethingThatLogsUsingLogger();
return input?.ToString();
}
}
}
That service code simply attempts to log using ILogger
using Microsoft.Extensions.Logging;
using System;
namespace AWSLambda3.Services
{
public class ServiceName1 : IServiceName1
{
private readonly ILogger<ServiceName1> _logger;
public ServiceName1(ILogger<ServiceName1> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void DoSomethingThatLogsUsingLogger()
{
_logger.LogTrace("test LogTrace log string");
_logger.LogDebug("test LogDebug log string");
_logger.LogInformation("test LogInformation log string");
_logger.LogWarning("test LogWarning log string");
_logger.LogError("test LogError log string");
_logger.LogCritical("test LogCritical log string");
}
}
}
However, upon deploying this lambda function handler to AWS Lambda, no logs are created in CloudWatch.
What am I missing?
Solution shown below in diff
Also need to ensure this package is installed
<PackageReference Include="Amazon.Lambda.Logging.AspNetCore" Version="3.0.1" />

.NET 2.0 Console App - Reading app settings

I am trying to get a .NET console application (Core 2.0) to read from an appsetting.json file. I have a webapi project that works fine with the following:
services.Configure<WebAppSettings>(Configuration.GetSection("WebAppSettings"));
But in the console app it says:
'ServiceCollection' does not contain a definition for 'Configure' and no extension method 'Configure' accepting a first argument of type 'ServiceCollection' could be found (are you missing a using directive or an assembly reference?)
I have the following packages installed in the console app project:
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
Here is the full Program.cs class:
using System;
using System.IO;
using Cam.Checker.Services;
using Cam.Common;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Cam.Checker
{
class Program
{
public static IConfigurationRoot Configuration { get; set; }
static void Main(string[] args)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
//.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);
Configuration = builder.Build();
var services = new ServiceCollection();
services.AddTransient<ICheckerService, CheckerService>();
// app settings
services.Configure<WebAppSettings>(Configuration.GetSection("WebAppSettings"));
var provider = services.BuildServiceProvider();
}
}
}
Thank you!
You need to add the NuGet package Microsoft.Extensions.Options.ConfigurationExtensions to get the Configure extension method.

Nancy 500 server error with dotnetcore and kestrel

I am trying to use NancyFX (clint-eastwood) with dotnetcore1.1 and dotnet-cli 1.0.0-rc4-004771. My current project structure is -
CustomBootstrapper.cs
HomeModule.cs
index.sshtml
nancyapp.csproj
Program.cs
Startup.cs
And codes are -
nancyapp.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Owin">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Nancy">
<Version>2.0.0-clinteastwood</Version>
</PackageReference>
</ItemGroup>
</Project>
Program.cs
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace nancyapp
{
class Program
{
static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
Startup.cs
using Microsoft.AspNetCore.Builder;
using Nancy.Owin;
namespace nancyapp
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseOwin(x => x.UseNancy());
}
}
}
HomeModule.cs
using Nancy;
namespace nancyapp
{
public class HomeModule : NancyModule
{
public HomeModule()
{
Get("/", _ => { return View["index.sshtml"]; });
Get("/test/{name}", args => new Person() { Name = args.name });
}
}
public class Person
{
public string Name { get; set; }
}
}
index.sshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
Welcome to Nancy App.
</body>
</html>
CustomBootstrapper.cs is currently empty.
When I try to access Get("/test/{name}", args => new Person() { Name = args.name }); from a rest client i get the expected result.
However, when I try to access to root or Get("/", _ => { return View["index.sshtml"]; });, I get a 500 server error saying -
Error details are currently disabled. To enable it, please set
TraceConfiguration.DisplayErrorTraces to true. For example by
overriding your Bootstrapper's Configure method and calling
environment.Tracing(enabled: false, displayErrorTraces: true)
I tried following the instruction in the error message and enable error tracing by including the following code in CustomBootstrapper.cs
protected override void ApplicationStartup(Nancy.TinyIoc.TinyIoCContainer container, IPipelines pipelines)
{
var environment = GetEnvironment();
environment.Tracing(true, true);
}
But then I get the following error when trying to run the application with dotnet run
Unhandled Exception: System.ArgumentException: An item with the same
key has already been added. Key: Nancy.TraceConfiguration at
System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(Object key) at
System.Collections.Generic.Dictionary`2.Insert(TKey key,TValue value, Boolean add) at
nancyapp.CustomBootstrapper.ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) in D:\TempWork\nancyapp\CustomBootstrapper.cs:line 17 at
Nancy.Bootstrapper.NancyBootstrapperBase`1.Initialise() at
Nancy.Owin.NancyMiddleware.UseNancy(NancyOptions options) at
Nancy.Owin.DelegateExtensions.UseNancy(Action`1 builder, NancyOptionsoptions) at
nancyapp.Startup.<>c.<Configure>b__0_0(Action`1 x) in D:\TempWork\nancyapp\Startup.cs:line 10 at
Microsoft.AspNetCore.Builder.OwinExtensions.UseOwin(IApplicationBuilder builder, Action`1 pipeline) at
nancyapp.Startup.Configure(IApplicationBuilder app) in D:\TempWork\nancyapp\Startup.cs:line 10
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app) at
Microsoft.AspNetCore.Hosting.Internal.WebHost.BuildApplication() at
Microsoft.AspNetCore.Hosting.WebHostBuilder.Build() at
nancyapp.Program.Main(String[] args) in D:\TempWork\nancyapp\Program.cs:line 11
I am not sure what's causing the error or how to enable tracing. Can anyone help?
The are two problems here :
The 500 is because the view was not found , what you need to do is provide a root path by implementing IRootPathProvider and return Directory.GetCurrent().
Secondly to enable tracing you need public override void Configure(INancyEnvironment environment) this adds the keys hence you the exception you are getting.
You may get the same server error (500) when using Nancy in a .NET Core 3.1 app in combination with Owin >= v3.
I've solved the issue by downgrading Microsoft.AspNetCore.Owin from v3.x to v2.2.0.
My running setup was the following after the downgrade:
It's also fine to return a simple text for testing:
Get("/", _ => { return new TextResponse(HttpStatusCode.OK, "Hello world!"); });

Resources