SignalR Hub Clients not resolving correctly outside SignalR pipeline - c#

Please help, this is driving me crazy!
My Server -> SignalR (JS) Client method execution works fine via the SignalR pipeline, but not when called via a background service. I'm using ASP.NET MVC / Castle Windsor DI
I use a custom Dependency Resolver which I register during Owin Startup
I observe that via the NotificationHub (SignalR pipeline), Clients is resolved to a HubConnectionContext class:
However, via my background service, Clients resolves to a HubConnectionContextBase class:
... so I'm pretty sure it's a DI issue. I just can't see what I'm doing wrong. Also any tips to Debug would be greatly appreciated. Thanks
Application Start:
bootstrapper = ContainerBootstrapper.Bootstrap();
this.container = bootstrapper.Container;
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;
OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
DependencyResolverSignalR:
public class DependencyResolverSignalR : DefaultDependencyResolver
{
public static HubConfiguration CreateHubConfiguration()
{
var signalrDependencyResolver = new DependencyResolverSignalR(_container);
var configuration = new HubConfiguration {
EnableDetailedErrors = true,
Resolver = signalrDependencyResolver
};
return configuration;
}
private static IWindsorContainer _container;
public DependencyResolverSignalR(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
_container = container;
}
public override object GetService(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
}
}
NotificationService:
(runs via a loop every 10 seconds - AFTER client has connected)
// Not working
var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
var clients = hubContext.Clients;
clients.All.receiveMessage(testMessage);
NotificationHub:
public override Task OnConnected()
{
var connectionId = Context.ConnectionId;
...
...
// Working fine
base.Clients.All.receiveMessage(testMessage);
return base.OnConnected();
}
Client:
omitted since it works fine via the signalr pipeline
I would expect the receiveMessage() client method called via the NotificationService to be executed on the client in exactly the same way it does when called via the SignalR pipeline. Instead nada. No error message, the call just silently does nothing.
I've even tried following the guide here (though it's geared towards Ninject) SignalR document to resolve the Clients (IHubConnectionContext) directly. Exactly the same result.
Code:
var resolverSignalR = new DependencyResolverSignalR(container);
container.Register(Component
.For<IHubConnectionContext<dynamic>>()
.UsingFactoryMethod(() =>
resolverSignalR.Resolve<IConnectionManager().GetHubContext<NotificationHub>().Clients));

Solved! It was an issue with the HubConfiguration - I'm not sure what exactly. But just bypassing it (which is possible because I'm already replacing the DependencyResolver in Application_Start()) fixed the issue.
Old OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
Replaced by:
app.MapSignalR();
but make sure you have something like this in Application_Start() (or wherever you initialise your DI container)
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;

Related

Unable to resolve service for type 'System.Net.Http.HttpClient'

I created a ViewComponent class which call a REST API using the HttpClient, this is the code:
public class ProductsViewComponent : ViewComponent
{
private readonly HttpClient _client;
public ProductsViewComponent(HttpClient client)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
}
public async Task<IViewComponentResult> InvokeAsync(string date)
{
using(var response = await _client.GetAsync($"/product/get_products/{date}"))
{
response.EnsureSuccessStatusCode();
var products = await response.Content.ReadAsAsync<List<Products>>();
return View(products);
}
}
}
I get this error:
InvalidOperationException: Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate MyApp.ViewComponents.ProductsViewComponent'
I injected the HttpClient in the ConfigureService method available in Startup in this way:
services.AddHttpClient<FixturesViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
UPDATE:
I registered the ProductsViewComponent too, same error.
I had a similar problem - the problem was in double registration:
services.AddHttpClient<Service>();
services.AddSingleton<Service>(); // fixed by removing this line
Similar examples [just adding to clarify that it's not specific to AddSingleton, nor related to the order.]
services.AddScoped<IService, Service>(); // fixed by removing this line
services.AddHttpClient<IService, Service>();
TLDR;
ViewComponents do not support typed clients out of the box. To resolve this, add a call to AddViewComponentsAsServices() onto the end of the call to services.AddMvc(...).
After a pretty long chat that ran off the back of being able to reproduce your issue, we determined initially that the problem being observed is specific to ViewComponents. Even with a call to IServiceCollection.AddHttpClient<SomeViewComponent>(), passing an instance of HttpClient into SomeViewComponents constructor just refused to work.
However, sitting a new class (SomeService) between SomeComponent and HttpClient works as expected. This is what the docs refer to as a typed client. The code looks a bit like this:
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeService>();
// ...
}
// SomeService.cs
public class SomeService
{
public SomeService(HttpClient httpClient)
{
// ...
}
}
// SomeViewComponent.cs
public class SomeViewComponent
{
public SomeViewComponent(SomeService someService)
{
// ...
}
}
As I've already stated, this approach works - the ASP.NET Core DI system is very happy to create the instance of SomeService and its typed HttpClient instance.
To restate the original problem, take the following example code:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(HttpClient httpClient)
{
// ...
}
}
In this case, the ASP.NET Core DI system refuses to create an instance of SomeViewComponent due to not being able to resolve HttpClient. It turns out that this is not specific just to ViewComponents: it also applies to Controllers and TagHelpers (thanks to Chris Pratt for confirming for TagHelpers).
Interestingly, the following also works:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient("SomeViewComponent")
// ...
}
}
In this example, we're taking advantage of the fact that the call to AddHttpClient<SomeViewComponent> registered a named client for us.
In order to be able to inject HttpClient directly into a ViewComponent, we can add a call to AddViewComponentsAsServices when we register MVC with DI:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(...)
.AddViewComponentsAsServices();
// ...
}
AddControllersAsServices and AddTagHelpersAsServices can also be called to add the same support for Controllers and TagHelpers respectively.
If we look at the docs more closely, it's clear that none of the examples there inject a HttpClient into Controllers et al - there's simply no mention of this approach at all.
Unfortunately, I don't know enough about the ASP.NET Core DI system in order to be able to explain exactly why this works the way it does: The information I've provided above simply explains the what along with a solution. Chris Pratt has opened an issue in Github for the docs to be updated to expand upon this.
I was getting a similar error in my Azure Function Version 2. As per this document, we should be able to add the IHttpClientFactory as a dependency. After adding this DI in my Azure Function, I was getting the error mentioned below.
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to
resolve service for type 'System.Net.Http.IHttpClientFactory' while
attempting to activate
'OServiceBus.Adapter.FetchDataFromSubscription1'
The issue was that I had not override the Configure function to add the HttpClient as a registered dependency. So I just created a class called Statup in the root directory of my Azure Function as follows.
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(ServiceBus.Adapter.Startup))]
namespace ServiceBus.Adapter {
public class Startup: FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
builder.Services.AddHttpClient();
}
}
}
After adding this, my function started working properly. Hope it helps.
I had a similar error message trying to inject a wrapper for an external REST service to my controller as an interface. I needed to change the following in ConfigureServices:
services.AddHttpClient<IMyServiceWrapper>("MyServiceWrapper", client =>
{
client.BaseAddress = new Uri("http://some_service/api");
}
to
services.AddHttpClient<IMyServiceWrapper, MyServiceWrapper>("MyServiceWrapper", client =>
{
client.BaseAddress = new Uri("http://some_service/api");
}
in order to be able to use the interface in the constructor of my controller:
public MyController(IMyServiceWrapper myService)
{
_myService = myService;
}
Useful for testing myController using a mock service.
It seems that you've got two view components mixed up. You're registering the FixturesViewComponent as a "named HTTP client" yet you attempt to inject an HttpClient instance in the ProductsViewComponent.
Changing the HttpClient registration to ProductsViewComponent should help:
services.AddHttpClient<ProductsViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
Maybe it will help, but in my situation this worked:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService,MyService>(); // my usual DI injection of a service that can be mocked
services.AddHttpClient<IMyService,MyService>(client => {
client.BaseAddress = new Uri("https://myservice.com/api");
}); // notice that I use IMyService for the reference of the registration AND implementation to where it will be injected.
}
public class MyService
{
public MyService(HttpClient client)
{
// client.BaseAddress is properly set here
}
}
public class MyController : Controller
{
public MyController(IMyService service) // used by the interface
{}
}
I've tried services.AddHttpClient<IMyService>() as well, which would not resolve due to lack of it's constructor.
Also tried services.AddHttpClient<MyService>() as above, but it would not resolve the configured instance, as described above.
So the important part is that class that is used to reference the resolved type needs to be used. So this also works:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<MyService>(); // registering the type itself, not via interface
services.AddHttpClient<MyService>(client => {
client.BaseAddress = new Uri("https://myservice.com/api");
}); // it's ok here, since it will be resolved by it's own type name
}
public class MyService
{
public MyService(HttpClient client)
{
// client.BaseAddress is properly set here
}
}
public class MyController : Controller
{
public MyController(MyService service) // used by the type directly
{}
}
It kind of makes sense, but documentation and examples could be better.

Nservicebus unity Endpoint failed to start

Using .NET 4.5.2, Visual studio 2017, C# 7.1, Unity, NServiceBus 6.
I receive the following error:
My application is a console app, here's some of the Program.cs code:
private static async Task ConfigureUnity()
{
IUnityContainer container = new UnityContainer();
var endpointConfiguration = new EndpointConfiguration("NSB.ChannelAdvisorService");
var transport = endpointConfiguration.UseTransport<LearningTransport>();
endpointConfiguration.AssemblyScanner().ExcludeAssemblies("netstandard");
endpointConfiguration.UseContainer<UnityBuilder>(
customizations =>
{
customizations.UseExistingContainer(container);
});
var endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
//register
container.RegisterType(typeof(IGenericHttpRequestRepository<>), typeof(GenericHttpRequestRepository<>), new TransientLifetimeManager());
container.RegisterType<IOrderRepository, OrderRepository>();
container.RegisterType<IShipmentRepository, ShipmentRepository>();
container.RegisterType<IOrderProcessService, OrderProcessService>();
container.RegisterType<IShipmentService, ShipmentService>();
container.RegisterInstance(endpointConfiguration);
//resolve
var orderProcessService = container.Resolve<IOrderProcessService>();
var shipmentService = container.Resolve<IShipmentService>();
.....
As you can see I'm using Unity and NServiceBus, this is to register DI and also use it withing NServicebus so i can DI it into my service to send a command.
The service trys to DI "IEndpointInstance"
public class OrderProcessService : IOrderProcessService
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private readonly IEndpointInstance _endpoint;
public OrderProcessService(IEndpointInstance endpoint)
{
_endpoint = endpoint;
}
public async Task PostNewOrderBatch()
{
var list = _orderRepository.GetBatchedOrders();
foreach(var item in list)// parallel this?
{
await _endpoint.Send(item.ToObject<ProcessBatchOrdersCommand>()).ConfigureAwait(false);
_orderRepository.DeleteFile(item.Property("FilePath").Value.ToString());
}
}
}
I get the feeling it could be an issue about the order of things, I don't think I've missed anything out as far as i can tell in some examples?
In NServiceBus v6 and later the endpoint instance is no longer automatically registered in the container. You need to register the endpoint instance returned from Endpoint.Start(configuration) on the existing container.
See https://docs.particular.net/nservicebus/dependency-injection/#using-an-existing-instance-endpoint-resolution

Register Service at Runtime via DI?

I am using ASP.NET Core and want to add a service to the IServiceProvider at runtime, so it can be used across the application via DI.
For instance, a simple example would be that the user goes to the settings controller and changes an authentication setting from "On" to "Off". In that instance I would like to replace the service that was registered at runtime.
Psuedo Code in the Settings Controller:
if(settings.Authentication == false)
{
services.Remove(ServiceDescriptor.Transient<IAuthenticationService, AuthenticationService>());
services.Add(ServiceDescriptor.Transient<IAuthenticationService, NoAuthService>());
}
else
{
services.Remove(ServiceDescriptor.Transient<IAuthenticationService, NoAuthService>
services.Add(ServiceDescriptor.Transient<IAuthenticationService, AuthenticationService>());
}
This logic works fine when I am doing it in my Startup.cs because the IServiceCollection has not been built into a IServiceProvider. However, I want to be able to do this after the Startup has already executed. Does anyone know if this is even possible?
Instead of registering/removing service at runtime, I would create a service factory that decides the right service at runtime.
services.AddTransient<AuthenticationService>();
services.AddTransient<NoAuthService>();
services.AddTransient<IAuthenticationServiceFactory, AuthenticationServiceFactory>();
AuthenticationServiceFactory.cs
public class AuthenticationServiceFactory: IAuthenticationServiceFactory
{
private readonly AuthenticationService _authenticationService;
private readonly NoAuthService _noAuthService;
public AuthenticationServiceFactory(AuthenticationService authenticationService, NoAuthService noAuthService)
{
_noAuthService = noAuthService;
_authenticationService = authenticationService;
}
public IAuthenticationService GetAuthenticationService()
{
if(settings.Authentication == false)
{
return _noAuthService;
}
else
{
return _authenticationService;
}
}
}
Usage in a class:
public class SomeClass
{
public SomeClass(IAuthenticationServiceFactory _authenticationServiceFactory)
{
var authenticationService = _authenticationServiceFactory.GetAuthenticationService();
}
}
Something of the sort is possible in Autofac:
private ILifetimeScope BeginChildScope()
{
return _lifetimeScope.BeginLifetimeScope(x =>
{
x.Register<IAuthenticationService>(b => new AuthenticationService());
});
}
using (var childScope = BeginChildScope())
{
// Do sth here
}
For .NET Core, I think this is the only possible solution atm.:
Best strategy for creating a child container (or isolated scope) with Microsoft.Extensions.DependencyInjection
Microsoft states unsupported features of ASP.NET Core DI here:
https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines#default-service-container-replacement

Signalr 2 Autofac calling Hub two to three times in a request

You'll have to excuse the strange title but after several hours of looking at the same issue, it's the best I could come up with!
I initially had signalr embedded with my MVC project but moved it out into an self-hosted OWIN application.
When ever a hub is called upon, on the initial load it will load the hub twice and subsequent calls it will load it three times.
Here is my hub, and I followed the documentation to the tee:
public class TestHub : Hub
{
private readonly ILifetimeScope _scope;
private ITestService _testService;
public TestHub(ILifetimeScope scope)
{
_scope = scope.BeginLifetimeScope();
_testService = _scope.Resolve<ITestService>();
}
public void SignalRTest()
{
var types = _testService.SomeMethod();
Clients.Caller.populateSignalRTest(types);
}
protected override void Dispose(bool disposing)
{
if (disposing && _scope != null)
{
_scope.Dispose();
}
base.Dispose (disposing);
}
}
Here is the OWIN configuration:
var listener = (HttpListener)appBuilder.Properties[typeof(HttpListener).FullName];
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
appBuilder.UseCors(CorsOptions.AllowAll);
var builder = new ContainerBuilder();
// ... Other modules being imported ...
builder.RegisterModule<NLogModule>();
builder.RegisterType<TestService> ().As<ITestService> ();
builder.RegisterHubs(Assembly.GetExecutingAssembly());
var config = new HubConfiguration();
var container = builder.Build();
config.Resolver = new AutofacDependencyResolver(container);
config.EnableDetailedErrors = true;
appBuilder.UseAutofacMiddleware(container);
appBuilder.MapSignalR("/signalr", config);
Autofac has to resolve all dependencies before running any methods - the application calls upon mulitple Hubs and this takes a long time to resolve.
Here are the versions I am using:
SignalR 2.1.2 (Also tried 2.2.0)
AutoFac 3.5.2
Has anyone come across this or know why this is happening?
Thanks
A colleague did a bit of debugging whilst I raged and did something else and discovered it was one of the services rather than AutoFac or signalR. We have an Index and the constructor method of the service was verifying the index was valid. Remove it and it was fine.

Self-Hosted SignalR app refusing CORS requests

I am working on an MVC 5 application that uses a windows service to perform some processing; I am using signal R so that I can show if the windows service is working on the UI and also allow the user to manually start processing (as opposed to running on a schedule).
On the server side I have the following config:
public class SignalRStartup
{
public static IAppBuilder App = null;
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
};
map.RunSignalR(hubConfiguration);
});
}
}
Which is used like so:
SignalR = WebApp.Start<SignalRStartup>(_settings.LoaderServiceUrl);
Right now the loader service url is: http://localhost:8080
Then on the client side:
var adminHubProxy = $.connection.adminHub;
adminHubProxy.client.updateProcessing = function(status) {
if (status === true) {
$('#processing').show();
} else {
$('#processing').hide();
}
};
$.connection.hub.url = 'http://localhost:8080/signalr';
$.connection.hub.start();
$('#startProcessingLink').on('click', function() {
adminHubProxy.server.startProcessing();
});
And if it matters the code that includes the generated proxy:
<script src="http://localhost:8080/signalr/hubs"></script>
So the problem I'm having is that when I trigger the startProcessing function the server throws back this error message:
XMLHttpRequest cannot load http://localhost:8080/signalr/send?transport=serverSentEvents&connectionTok…Pp7JqCJOnkJEA%3D%3D&connectionData=%5B%7B%22name%22%3A%22adminhub%22%7D%5D.
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
From all the reading I've done my configuration should be resolving this issue by allowing all CORS requests but it isn't and I can't see why.
Edit
After some more debugging I pulled up the details of the response on the negotiate call and am seeing the following headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost
I'm not sure why the credentials header is being added at all, but the origin header again makes me believe that this should be working.
I figured out the problem, first off the error message has absolutely nothing to do with what is going on.
TL;DR;
The problem was that the AdminHub could not be resolved on the server side because of my dependency injection setup
I am using Castle Windsor for dependency injection and originally the AdminHub looked like this:
public class AdminHub : Hub
{
private readonly IMyService _myService;
public AdminHub(IMyService myService)
{
_myService= myService;
_myService.OnProcessingUpdate += (sender, args) => UpdateProcessingStatus();
}
public void UpdateProcessingStatus()
{
Clients.All.updateProcessing(_myService.IsProcessing);
}
public void GetProcessingStatus()
{
Clients.Caller.updateProcessing(_myService.IsProcessing);
}
public void StartProcessing()
{
_myService.Process();
}
}
The default dependency resolver cannot resolve this as it requires a parameterless constructor. This answer both served to point out what was happening and provide the basis for a solution.

Categories