How to define signalR MapHub route in a multi-Tenant application - signalr

SignalR ChatHub is defined as a controller.
[Route("{tenantName}/chatHub")]
public class ChatHub: Hub
In startup configure method,
app.UseSignalR(routes =>
{
routes.MapHub<MessageHub>("/dev/chatHub");
});
This works fine in development environment.
My question is, how can useSignalR method be defined so that mapHub path is independent of the tenant or there is a different way to define so as to work on all the tenants like $"/{tenant}/chatHub"?

I've handled the issue in the following way;
Created chatHub controller without a tenant like
[Route("/chatHub")]
public class ChatHub: Hub
and in the startup.cs defined as
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chatHub");
});
So this way common instance is handling all the tenants call and identifying tenants by method arguments like
public async Task SendMessage(string tenant, string message)
{
...
}

I have been looking at this and if you are using Finbuckle as your Multitenant library you can use the following to specify your Route
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/{__tenant__}/chatHub");
});

Related

Consume OpenApi client .NET Core with Interface

Someone out there must have run into this already...
I created a WebApi solution with swagger implemented, full documentation, the whole 9 yards!
When I run my web api solution, see the swagger output (and I've tested the endpoints, all working fine)
I can see the swagger definition: https://localhost:5001/swagger/v1/swagger.json
Now, I want to consume this Api as a connected service on my web app.
So following every single tutorial online:
I go to my webapp
right click on Connected Services
Add Connected Service
Add Service Reference > OpenApi > add Url, namespace & class name
That generates a partial class in my solution (MyTestApiClient)
public parial class MyTestApiClient
{
// auto generated code
}
Next step, inject the service in Startup.cs
services.AddTransient(x =>
{
var client = new MyTestApiClient("https://localhost:5001", new HttpClient());
return client;
});
Then, inject the class into some class where it's consumed and this all works
public class TestService
{
private readonly MyTestApiClient _client; // this is class, not an interface -> my problem
public TestService(MyTestApiClient client)
{
_client = client;
}
public async Task<int> GetCountAsync()
{
return _client.GetCountAsync();
}
}
So everything up to here works. BUT, this generated OpenApi client doesn't have an interface which sucks for the purposes of DI and Unit Testing.
I got around this by creating a local interface IMyTestApiClient, added to the generated class (MyTestApiClient). I only have 1 endpoint in my WebApi so have to declare that on my interface.
public parial class MyTestApiClient : IMyTestApiClient
{
// auto generated code
}
public interface IMyTestApiClient
{
// implemented in generated MyTestApiClient class
Task<int> GetCountAsync();
}
services.AddTransient<IMyTestApiClient, MyTestApiClient>(x =>
{
IMyTestApiClient client = new MyTestApiClient("https://localhost:5001", new HttpClient());
return client;
});
public class TestService
{
private readonly IMyTestApiClient _client; // now injecting local interface instead of the generated class - great success
public TestService(IMyTestApiClient client)
{
_client = client;
}
public async Task<int> GetCountAsync()
{
return _client.GetCountAsync();
}
}
But this is a bad approach because it makes me manually create an interface and explicitly declare the methods I want to consume. Furthermore, every time my Api gets updated, I will have to tweak my local interface.
So question time:
How can I add an OpenApi Service Reference that automagically also generates an interface as well?
Thanks in advance for any help getting to a viable solution.
You may have already found the answer but I had the same issue and managed to resolve it by adding /GenerateClientInterfaces:true in the Options section for the OpenAPI reference in my .csproj:
<OpenApiReference Include="api.json" CodeGenerator="NSwagCSharp" Namespace="MyNamespace" ClassName="MyClassName">
<SourceUri>https://localhost:7040/swagger/v1/swagger.json</SourceUri>
<OutputPath>MyClient.cs</OutputPath>
<Options>/GenerateClientInterfaces:true</Options>
</OpenApiReference>

Blazor. Task from another service

I'm studying blazor server.
Deployed a solution from a standard vs template.
Created two server-side services, TestService1 and TestService2.
In TestService1 i have task
GetMyData()
How can i call with task from TestService2?
If i trying
var serv1 = new TestService1()
i have to fill in all the variables of the constructor that is in TestService1.
What is easiest way?
In line with the comment on your question, the best way to go about this in Blazor is to utilize the built-in dependency injection mechanism.
I assume that your services look like the following:
public class TestService1
{
public object GetMyData()
{
}
}
public class TestService2
{
private readonly TestService1 _testService1 { get; set; }
public class TestService2(TestService1 ts1)
{
_testService1 = ts1;
}
public void DoesSomething()
{
var data = _testService1.GetMyData();
//...
}
}
First, you'd need to register these with Blazor at startup, so in your Startup.cs in the ConfigureServices method, add the following, assuming you have an empty constructor available for TestService1:
services.AddSingleton<TestService1>();
Because you'll need to instantiate an instance of TestService1 into TestService2 to call methods on it, you'll have to handle registration of TestService2 differently since you'll need to procure an instance of TestService1 from the DI service to instantiate it:
services.AddSingleton<TestService2>(s => {
var testService1 = s.GetService<TestService1>();
return new TestService2(testService1);
});
It's possible you may need to scope the services differently (e.g. used scoped instead of singletons). You can read about the differences here.
Now something is presumably calling TestService2 to kick all this off, so let's pretend it's running in a component in your Blazor app. You'd inject TestService2 into the component with the following:
#inject TestService2 _testService2
<h1>Hello!</h1>
#code {
protected override void OnInitialized()
{
_testService2.DoesSomething();
}
}
As part of the initialization then of this component, it'll automatically inject a TestService2 instance (based on the scoping you specified at DI initialization) to your component and will call the DoesSomething method on it. When injected, it looks to DI to instantiate the TestService1 service to the constructor as you've also specified, leaving it free to call that method and the call commences as intended.
Please let me know if you'd like any clarification somewhere!

Call SignalR Hub From Another .Net Project

I've got SignalR hubs in one .NetCore project and SignalR clients in another .NetCore project (to comply with internal infrastructure guidelines). I'm struggling with how to implement the client code that will provide the connection to the hubs. All my projects build fine, I'm just missing the connectivity part.
Client Project:
public class MyClientController
{
private readonly IHubContext<MyHub, IMyHubClient> _hub;
public MyClientController(IHubContext<MyHub, IMyHubClient> hub)
{
_hub = hub;
// THIS NEVER GETS CALLED/HIT BECAUSE I DON'T KNOW HOW TO REGISTER IT
_hub.Clients.All.BroadcastMessage("Notify", $"Hello everyone. This has been constructed");
}
}
I'm guessing I need to do some configuration in the Startup.Configure() method? I've installed the client package already,
EDIT: I added the following code, but it's complaining about the format (remember, this is not a relative path, it's in another service).
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<MyHub>("http://localhost:60913/myHub");
}
Am I on the right path here?
The connection is made by mapping a route to your hub class. The docs have a good example. This includes:
// Add to services collection
services.AddSignalR();
// Map the route "/chathub" to a ChatHub class
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chathub");
});
By the way, a hub can be a standalone class not associated to a controller.

Send messages from server to client via SignalR in .NET Core

I want to send messages from the server (from a class, not a controller) via the SignalR Hub.
The hub works for messages originating from the client but not for messages from the server.
I've tried multiple methods of which non seem to work. For example, I tried retrieving the hub context using:
GlobalHost.ConnectionManager.GetHubContext<MyHub>()
with no success.
What is the best and up-to-date method of doing this in .NET Core?
Temporary Solution:
Having a websocket client inside the host api. Then making it connect to itself. This is not an ideal solution but works as a temporary fix.
You can inject the context in your class as service. Your class must be initialized via DI and added as a service. There is no difference between class or controller.
public class SomeClass
{
public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }
public SomeClass(IHubContext<ChatHub, IChatClient> chatHubContext)
{
_strongChatHubContext = chatHubContext;
}
public async Task SendMessage(string message)
{
await _strongChatHubContext.Clients.All.ReceiveMessage(message);
}
}
You can also get service like following by injecting IHttpContextAccessor
var _strongChatHubContext = httpContextAccessor.HttpContext.RequestServices.GetRequiredService<IHubContext<ChatHub, IChatClient>>()
reference:
https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.1
First you need to somewhere instantiate your hub (normally when your app is bootstrapping).
MyHub myHub = new MyHub();
Then on your class inject the context:
private readonly IHubContext<NotifyHub, ITypedHubClient> hubContext;
And in your class method just call the hub:
hubContext.Clients.All.yourHubMethod(yourPayload);

Access registered object in Nancy Startup class

I'm writing a service in Nancy and I'm using some middleware for monitoring the service:
app.UseOwin(buildFunc =>
{
var log = ConfigureLogger();
buildFunc.UseMonitoringAndLogging(log, HealthCheck);
buildFunc.UseNancy();
});
The middleware is configured to use a HealthCheck() function defined in the Startup class as:
public async Task<bool> HealthCheck()
{
return await SomeRepo.HealthCheck();
}
SomeRepo has a HealthCheck() method that queries the database to confirm it is available/responding. But how to inject SomeRepo into the Startup class, or alternatively access the container to resolve SomeRepo?
At this point you are still in Owin and not in the Nancy pipeline. What host are you running on ? If you are using aspnetcore the you can register your deps in RegisterServices() method and it will handle the injection into your method. You can use Autofac or StructureMap so you can share your container with Nancy as well like this.

Resources