Getting current hub in SignalR - signalr

Is there a good way to call methods in SignalR hub from a controller ?
Right now I have this:
public class StatsHub : Hub
{
private static readonly Lazy<StatsHub> instance = new Lazy<StatsHub>(() => new StatsHub());
public static StatsHub Instance { get { return instance.Value; } }
public StatsHub()
{
if (this.Clients == null)
{
var hubContext = SignalR.GlobalHost.ConnectionManager.GetHubContext<StatsHub>();
this.Clients = hubContext.Clients;
this.Groups = hubContext.Groups;
}
}
// methods here...
}
so in my controller actions I can just say, for example
StatsHub.Instance.SendMessage("blah");
and it's almost good, except that hubContext doesn't have Caller or Context properties of Hub - which are nice to have.
Hopefully, there's a better way to do this ?

If you want to broadcast over a hub from outside of the hub, you need GlobalHost.ConnectionManager.GetHubContext<MyHub>() to get ahold of the hub context. You can then use this context to broadcast via the .Clients property.
As indicated in your sample code you already get ahold of the hub context, but doing so inside the hub just doesn't feel right in my opinion. If you're only using the logic in SendMessage() from your controller actions, I'd move the code right into the controller action and use the hub context obtained via GetHubContext<T>() from there.
Please note that the Caller or Context property will always be null in this scenario, because SignalR wasn't involved when making a request to the server and therefore cannot provide the properties.

Found a DefaultHubManager, which is what I need, I think.
DefaultHubManager hd = new DefaultHubManager(GlobalHost.DependencyResolver);
var hub = hd.ResolveHub("AdminHub") as AdminHub;
hub.SendMessage("woohoo");
Works. If there's an even better/preferred way - please share.

As per the latest documentation, IHubContext can be injected by dependency injection.
documentation : https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-6.0
In service you could do
public class NotificationService : INotificationService
{
private readonly IHubContext<NotificationHub> _hubContext;
public NotificationService(IHubContext<NotificationHub> hubContext)
{
_hubContext = hubContext;
}
}
In controller
public class HomeController : Controller
{
private readonly IHubContext<NotificationHub> _hubContext;
public HomeController(IHubContext<NotificationHub> hubContext)
{
_hubContext = hubContext;
}
}
Once you have HubContext you could send message to group/client etc.
public async Task SendMessage()
{
return await _hubContext.Clients.All.SendAsync("Notify", $"Hello world");
}

Related

How to correctly use dependency Injection with .Net Core SignalR

I'm learning .Net Core SignalR and investigating how I could use it with my app live charts. I play with some examples on the net and they all work, but I don't know how to use SignalR with database polling. I'm getting below error:
Cannot access a disposed object ...
I'm assuming it is related to my contex is being disposed after request is completed. I'm using dependency injection.
ChatController
public class ChatController : ControllerBase
{
private IChatService _chatService;
private IChatContext<ChatHub> _hub;
public ChatController(IChatContext<ChatHub> hub, IChatService chatService)
{
_hub = hub;
_chatService = chatService;
}
public IActionResult Get()
{
var timerManager = new TimerManager(() => _hub.Clients.All.SendAsync("transferchatdata", _chatService.ChatDataByProds()));
return Ok(new { Message = "Request Completed" });
}
}
ChatService
public interface IChatService
{
IEnumerable<ChatDataByProd> ChatDataByProds();
}
public class ChatService : IChatService
{
private ChatContext _context;
public ChatService(ChatContext context)
{
_context = context;
}
public IEnumerable<ChatDataByProd> ChatDataByProds()
{
return _context.ChatDataByProds;
}
}
ChatHub
public class ChatHub : Hub
{
}
It seems that you are using a EF Core context that is a scoped lifetime service, which means per-request lifetime in ASP.NET Core. Your ChatService must have a longer lifetime than a HTTP request, and a single instance of the database context would be disposed by the container while you are still holding the reference to it.
Thus, you need to obtain an IServiceProvider container in the ctor of ChatService, and GetService the database context each time when you need to access the database.

Unity Container usage With multiple implementation

I have come up with a Question, that what is the usage of Unity Container or NInject if it is handled only for a single instance of an interface.
Ex: Generally we use like this
Public Class IEmailSender
{
Public Void SendEmail();
}
Public Class SMTP: IEmailSender
{
Public Void SendEmail()
{
// Send Email Logic using SMTP
}
}
Public Class OtherSender: IEmailSender
{
Public Void SendEmail()
{
// Send Email Logic for Other Sender
}
}
Public Class Builder
{
Public Static IEmailSender CreateBuilder(string senderType)
{
if(senderType.Equals("SMTP"))
{
Return New SMTP();
}
ElseIf(senderType.Equals("OTHER"))
{
Return New OtherSender();
}
}
}
In my Screen have two buttons
#1. Send from SMTP - Event(EventArgs)
#2. Send from Other Sender - Event(EventArgs)
Have the Same Logic in two Methods
IEmailSender emailSender = Builder.CreateBuilder(button.CommandArgument)
emailSender.sendEmail();
So, How these different scenarios will be handled with Unity Configuration in Unity Block
or NInject Binder,
Your feedback will be highly appreciatable.
In Unity, You can use named instances to register and retrieve different implementations of a single interface.
First You need to register concrete types to the interface using apriopriate names:
var container = new UnityContainer();
container.RegisterType<IEmailSender, SmtpSender>("SMTP");
container.RegisterType<IEmailSender, OtherSender>("OTHER");
After this you can use it in Your code:
IEmailSender emailSender = container.Resolve<IEmailSender>(button.CommandArgument);
emailSender.SendEmail();

Signalr: getting hubcontext with unity di

I started using unity to inject stuff into one of my hubs. This works, but, when I resolve a hubcontext somewhere outside my hub it seems like i'm getting the wrong one. This is because when I try to send messages with it, connected clients don't get them.
Dependencyresolver:
public class SignalRUnityDependencyResolver : DefaultDependencyResolver, IDependencyResolver
{
private IUnityContainer _container;
public SignalRUnityDependencyResolver(IUnityContainer container)
{
_container = container;
}
public override object GetService(Type serviceType)
{
if (_container.IsRegistered(serviceType)) return _container.Resolve(serviceType);
else return base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
if (_container.IsRegistered(serviceType)) return _container.ResolveAll(serviceType);
else return base.GetServices(serviceType);
}
}
What I register in my container:
container.RegisterType<MessageHub>(new InjectionConstructor(new EFAuthRepository()));
My hub:
public class MessageHub : Hub
{
IAuthRepository _repository;
public MessageHub(IAuthRepository repository)
{
_repository = repository;
}
How I resolve outside my hub:
var context = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
Is there anything else that needs to be registred?
You should use the instance of the resolver to get the IConnectionMananger from the container as explained here.
Althought it is for Ninject you can get the idea. (look at at the bottom). This ConnectionManager can get you the HubContext as you were used to via GlobalHost.
resolver.Resolve<IConnectionManager>().GetHubContext<MessageHub>()
We made a simple example to ilustrate this (Ninject and Unity examples using signalr) and you can check it out here. If you are eager for the setup, it is here. ;)
Let me know if it helps.

Use Hub methods from controller?

I am using SignalR 2 and I can not figure out how I can use my Hub methods e.g from inside a controller action.
I know I can do the following:
var hub = GlobalHost.ConnectionManager.GetHubContext<T>();
hub.Clients.All.clientSideMethod(param);
But that executes the method directly on the client side.
What if I have business logic inside my server side ClientSideMethod(param) method I want to call from my controller the same way as when it is called from the client side?
At the moment I use public static void ClientSideMethod(param) inside my hub and in that method I use the IHubContext from the ConnectionManager.
Is there no better was of doing this?
The following is not working (anymore in SignalR 2?):
var hubManager = new DefaultHubManager(GlobalHost.DependencyResolver);
instance = hubManager.ResolveHub(typeof(T).Name) as T;
instance.ClientSideMethod(param);
There I get a "Hub not created via Hub pipeline not supported" exception, when accessing the Clients.
It might work to create a "helper" class that implements your business rules and is called by both your Hub and your Controller:
public class MyHub : Hub
{
public void DoSomething()
{
var helper = new HubHelper(this);
helper.DoStuff("hub stuff");
}
}
public class MyController : Controller
{
public ActionResult Something()
{
var hub = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
var helper = new HubHelper(hub);
helper.DoStuff("controller stuff");
}
}
public class HubHelper
{
private IHubConnectionContext hub;
public HubHelper(IHubConnectionContext hub)
{
this.hub = hub;
}
public DoStuff(string param)
{
//business rules ...
hub.Clients.All.clientSideMethod(param);
}
}
As I did not find a "good solution" I am using #michael.rp's solution with some improvements:
I did create the following base class:
public abstract class Hub<T> : Hub where T : Hub
{
private static IHubContext hubContext;
/// <summary>Gets the hub context.</summary>
/// <value>The hub context.</value>
public static IHubContext HubContext
{
get
{
if (hubContext == null)
hubContext = GlobalHost.ConnectionManager.GetHubContext<T>();
return hubContext;
}
}
}
And then in the actual Hub (e.g. public class AdminHub : Hub<AdminHub>) I have (static) methods like the following:
/// <summary>Tells the clients that some item has changed.</summary>
public async Task ItemHasChangedFromClient()
{
await ItemHasChangedAsync().ConfigureAwait(false);
}
/// <summary>Tells the clients that some item has changed.</summary>
public static async Task ItemHasChangedAsync()
{
// my custom logic
await HubContext.Clients.All.itemHasChanged();
}

WCF Runtime Error while using Constructor

I am new to WCF i am using constructor in my WCF service.svc.cs file....It throws this error when i use the constructor
The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor.
To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.
When i remove the constructor its working fine....But its compulsory that i have to use constructor...
This is my code
namespace UserAuthentication
{
[ServiceBehavior(InstanceContextMode=System.ServiceModel.InstanceContextMode.Single)]
public class UserAuthentication : UserRepository,IUserAuthentication
{
private ISqlMapper _mapper;
private IRoleRepository _roleRepository;
public UserAuthentication(ISqlMapper mapper): base(mapper)
{
_mapper = mapper;
_roleRepository = new RoleRepository(_mapper);
}
public string EduvisionLogin(EduvisionUser aUser, int SchoolID)
{
UserRepository sampleCode= new UserRepository(_mapper);
sampleCode.Login(aUser);
return "Login Success";
}
}
}
can anyone provide ideas or suggestions or sample code hw to resolve this issue...
You could add something like (if possible):
public UserAuth() : this(SqlMapperFactory.Create())
{
}

Resources