Mapping Generic Hub [duplicate] - signalr

I'm working on a simple .NET 6 application to enable data update notifications in our front-end application. I've built something similar in .NET 5 before, but I'm running across a DI issue that's got me stumped. In 5, all hubs that were mapped automatically have an IHubContext that is set up in the container for them as well. That doesn't appear to be the case anymore in 6.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNet.SignalR.IHubContext`1[SignalRNotifications.Hubs.NotificationHub]' while attempting to activate 'SignalRNotifications.Controllers.NotificationController'.
The new non-startup DI in 6 looks weird to me, but I'm just not seeing anything available that says how to fix it. Any suggestions on how to get an IHubContext to inject into my controller?
Thanks!
Update: Here is some pertinent code:
using Microsoft.AspNetCore.Builder;
using SignalRNotifications.Hubs;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSignalR().AddAzureSignalR();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<NotificationHub>("/NotificationHub");
});
app.Run();
Dependency injection is done in the controller in the most predictable of ways:
namespace SignalRNotifications.Controllers
{
[AllowAnonymous]
[Route("api/[controller]")]
[ApiController]
public class NotificationController : ControllerBase
{
private readonly IHubContext<NotificationHub> _notificationContext;
public NotificationController(IHubContext<NotificationHub> notificationContext)
{
_notificationContext = notificationContext;
}

System.InvalidOperationException: Unable to resolve service for type
'Microsoft.AspNet.SignalR.IHubContext`1[SignalRNotifications.Hubs.NotificationHub]'
while attempting to activate
'SignalRNotifications.Controllers.NotificationController'.
The issue might be related to you having installed the wrong version of SignalR and adding the wrong namespace reference. You are using Microsoft.AspNet.SignalR.IHubContext, instead of Microsoft.AspNetCore.SignalR.IHubContext.
According to your code and refer to the Asp.net Core SignalR document, I create a sample and inject an instance of IHubContext in a controller, everything works well. But I notice that when using the IHubContext, we need to add the using Microsoft.AspNetCore.SignalR; namespace, like this:
So, please check your code and try to use:
using Microsoft.AspNetCore.SignalR;

Related

Verisioning a Webapi .net-core using with VersionByNamespaceConvention and not being forced to use RouteAttribute on Controllers

The idea is to have a versioned API by URL like this:
/api/v1/weatherforecast
/api/v2/weatherforecast
/api/v3/weatherforecast
My namespace is the following:
On the Startup I have this code to register the versioning:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddApiVersioning(
options =>
{
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
options.ReportApiVersions = true;
// automatically applies an api version based on the name of the defining controller's namespace
options.Conventions.Add(new VersionByNamespaceConvention());
});
}
So I wanted to remove the "[Route("api/v{version:apiVersion}/[controller]")]" Attribute in every Controller is this possible?
I was trying to map the controllers using something like this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "api",
pattern: "api/v{version:apiVersion}/{controller}/{id?}"
);
});
}
But when I run the code it gives the following error:
Action 'ApiVersioning.Api.V3.Controllers.WeatherForecastController.Get (ApiVersioning)' does not have an attribute route. Action methods on controllers annotated with ApiControllerAttribute must be attribute routed.'
So can I add an implicit routing convention for all Controllers that removes the need of the RouteAttribute on top of every Controller?
The error message you are seeing is a restriction of the built-in API Explorer. It only supports Attribute Routing. If you don't need that for documentation, then it shouldn't be a problem.
Unlike the ASP.NET of old, there is no distinction between a UI Controller and an API Controller. However, there are certain behaviors developers expect between them. API Versioning requires that all controllers are versioned, even if implicitly via the default API version (and otherwise undecorated). This behavior was undesirable for those mixing UI and APIs together. Once the [ApiController] attribute was introduced, there was finally a way to reliably identify whether a controller is for an API.
To test this, you can simply change ApiVersioningOptions.UseApiBehavior = false. This will revert back to the old behavior. Bear in mind that this will result in all controllers being versioned.
The alternate approach is to implement IApiControllerSpecification or IApiControllerFilter. Typically you only need IApiControllerSpecification as the filter is an aggregation of all specifications. Register your specification(s) or filter with the DI container. The implementation of a specification decides whether a controller is for APIs given a ControllerModel. There is a built-in specification that looks for [ApiController]. You might create one that looks for ControllerBase as the base class or use some other mechanism such as prefix in the route template or containing namespace. The choice is up to you. The specification will allow you to use your own conventions without having to use [ApiController].
You could also use a base class for all controllers with the attribution you want in a single place. It's not exactly the same as using a standard route convention, but it will achieve a similar result. You may need that approach if you expect to support API documentation.

Bind SignalR IHubContext to Ninject kernel in net core 2.1

I'm using NET Core 2.1 and have Ninject as my DI and I'm trying to inject IHubContext inside a controller constructor using ninject, but getting this error:
"Ninject.ActivationException: 'Error activating IHubContext No matching bindings are available, and the type is not self-bindable."
I've added SignalR to services (services.AddSignalR()) and mapped it for the IApplicationBuilder:
app.UseSignalR(route => route.MapHub("/myhub"));
Tried binding it from the global resolver also: kernel.Bind< IHubContext>().ToMethod(context => GlobalHost.DependencyResolver.Resolve< IHubContext>()).InSingletonScope(); but with no luck, what I am missing in order to inject this IHubContext in the constructor of my controller using Ninject.
Thank you.
Finally fixed this issue, for anyone else who has this problem, trying to use signalR with Ninject as the DI.
var serviceProvider = app.ApplicationServices;
kernel.Bind<IHubContext<MyHub>>().ToMethod(context => serviceProvider.GetService<IHubContext<MyHub>>());

Registering log4net named logger .NET Core service provider

I am struggling with finding a way to register log4net ILog within IServiceCollection services (.NET Core 2.1).
log4net LogManager provides a method that takes a Type as parameter so that it can be used when logging events. Here is an example call:
var log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
However when registering it with services at Startup method, I can use factory resolver, but still the type will be always the same. See the following excerpt:
private void SetServices(IServiceCollection services)
{
services.AddTransient(svc => CreateLog(MethodBase.GetCurrentMethod().DeclaringType)).;
}
protected ILog CreateLog(Type type)
{
return LogManager.GetLogger(type);
}
I was expecting that svc (IServiceProvider) will expose some ways to get to the type being actually resolved but there doesn't seem to be anything.
Also using reflection won't help (IMO, but I could be wrong) because ILog is activated prior to calling the resolved type ctor.
But maybe there is any extension on either MS side or log4net that would help resolving a named logger as explained on the beginning?
This is all to avoid having static members of ILog in any class that uses logging facility and use dependency injection instead.
Thanks, Radek
The way to do it is to implement and then register your own strongly typed Logger class that derives from ILogger.
.Net Core allows from to register generic type interface implementation with no type specified. In this case it would be:
services.Add(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(MyLogging.Logger<>)));
This allows all classes that require logging to use constructor injection as follows:
class A
{
public A(Ilogger<A> logger) {...}
}
Implementation of ILogger which a wrapper to log4net should be rather simple. Please let me know if you need an example.
Radek

How do you get HubContext in SignalR 3?

In SignalR 2 you could do something like this (taken from my blog):
var stockTickerHub = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
That allows you to get a reference to the SignalR hub from outside the hub (e.g. from a stock ticker thread).
This does not seem to be available in SignalR 3. How do you achieve the equivalent functionality in the new and upcoming SignalR?
I asked the same thing to the creator of SignalR, David Fowler on Jabbr, a forum where the creators of SignalR and the architects of ASP.NET 5 hang on from time to time, and his answer to this question was to use dependency injection.
While I haven't tried it yet with SignalR 3, I am pretty sure you can inject an instance of ConnectionManager that implements IConnectionManager in your class, and use it just like you would use GlobalHost to resolve your hub context.
Again, I have not done this with SignalR3, but I hope this will get you a little closer to finding a solution.
I put together a sample for using SignalR 2 with Autofac. (In this repo I use Autofac to inject dependencies in my hub, but also to inject instances of a ConnectionManager in other classes to get the hub context).
Hope this helps. Best of luck!
Dependency injection is indeed the way and works.
Example:
public class ChatController : Controller
{
readonly IConnectionManager _connectionManager;
public ChatController(IConnectionManager connectionManager)
{
_connectionManager = connectionManager;
}
public IActionResult Chat(string message)
{
IHubContext context = _connectionManager.GetHubContext<ChatHub>();
IConnection connection = _connectionManager.GetConnectionContext<PersistentConnection>().Connection;
context.Clients.All.NewMessage(message);
return new EmptyResult();
}
}
From every example I have seen and the few SignalR 3 apps I have implemented, you no longer have a strongly typed reference to your hub. The current methodology connects to a hub via the hub's name and URL. The On generic method creates a subscription to broadcasts from that hub and the method name you provide.
HubConnection connection = new HubConnection(ServerURL);
IHubProxy hubProxy = connection.CreateHubProxy("StockTickerHub");
hubProxy.On<StockTickerMessage>("[Your method name here]", msg => {
//your UI update logic here
});

Configuring dependency injection with ASP.NET Web API 2.1

I'm creating an ASP.NET Web API 2.1 site and as I want to inject dependencies directly into the controllers, I've created my own implementation of IDependencyResolver so that StructureMap will handle that for me.
public class StructureMapDependencyResolver : IDependencyResolver
{
public IDependencyScope BeginScope()
{
return this;
}
public object GetService(Type serviceType)
{
return ObjectFactory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return ObjectFactory.GetAllInstances(serviceType).Cast<object>();
}
public void Dispose()
{
}
}
I've then told Web API to use this class by adding this line to the Application_Start method in Global.asax
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver();
That compiled but when I tried to access any of the API methods in a browser I got an error like this
No Default Instance defined for PluginFamily System.Web.Http.Hosting.IHostBufferPolicySelector, System.Web.Http
That one was relatively easy to solve as I added a line to my StructureMap configuration
this.For<IHostBufferPolicySelector>().Use<WebHostBufferPolicySelector>();
However then I got other similar errors for other System.Web.Http classes and while I could resolve some of them I am stuck on how to deal with 3 of them, namely ITraceManager, IExceptionHandler and IContentNegotiator.
The issue is that TraceManager which seems to be the default implementation of ITraceManager is an internal class and so I can't reference it in my StructureMap configuration.
So am I going about this completely the wrong way or is there some other way to inject these internal classes?
I'd like to give you a suggestion and explanation why not to go this way, and how to do it differently (I'd even say better and properly).
The full and complete explanation of the inappropriate IDependencyResolver design could be found here: Dependency Injection and Lifetime Management with ASP.NET Web API by Mark Seemann
Let me cite these essential parts:
The problem with IDependencyResolver
The main problem with IDependencyResolver is that it's essentially a Service Locator. There are many problems with the Service Locator anti-pattern, but most of them I've already described elsewhere on this blog (and in my book). One disadvantage of Service Locator that I haven't yet written so much about is that within each call to GetService there's no context at all. This is a general problem with the Service Locator anti-pattern, not just with IDependencyResolver.
And also:
...dependency graph need to know something about the context. What was the request URL? What was the base address (host name etc.) requested? How can you share dependency instances within a single request? To answer such questions, you must know about the context, and IDependencyResolver doesn't provide this information.
In short, IDependencyResolver isn't the right hook to compose dependency graphs. **Fortunately, the ASP.NET Web API has a better extensibility point for this purpose. **
ServiceActivator
So, the answer in this scenario would be the ServiceActivator. Please take a look at this answer:
WebAPI + APIController with structureMap
An example of the ServiceActivator:
public class ServiceActivator : IHttpControllerActivator
{
public ServiceActivator(HttpConfiguration configuration) {}
public IHttpController Create(HttpRequestMessage request
, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
return controller;
}
}
All we can do with StructureMap, is in place. The key features of the Web API framework are still in place... we do not have to hack them. And we are also rather using DI/IoC then Service locator
Just try using UnityHierarchicalDependencyResolver instead of the other one. It worked for me. This is for future reference if somebody would like to use Unity

Resources