Storing the connection id of a specific client in SignalR hub - c#

Below you can see a simplified version of my SignalR self hosted hub on a windows service:
public static class SubscriptionHandler
{
public static int PriceFeedMembersCount = 0;
}
public class PriceHub : Hub
{
public Task SubscribeToPriceFeed()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<PriceHub>();
if (SubscriptionHandler.PriceFeedMembersCount == 0)
{
context.Clients.All.updatePriceSubscriptionStatus(true);
}
SubscriptionHandler.PriceFeedMembersCount++;
return context.Groups.Add(Context.ConnectionId, "PriceFeed");
}
public Task UnsubscribeFromPriceFeed()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<PriceHub>();
SubscriptionHandler.PriceFeedMembersCount--;
if (SubscriptionHandler.PriceFeedMembersCount == 0)
{
context.Clients.All.updatePriceSubscriptionStatus(false);
}
return context.Groups.Remove(Context.ConnectionId, "PriceFeed");
}
public void NotifySubscribers(Price price)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<PriceHub>();
context.Clients.Group("PriceFeed").updatePrice(price);
}
}
And I have two types of clients for that hub: One of them is web applications and the other one is windows services. Here you can see a demo implementation for my windows service as a signalr client:
public partial class WinSer45 : ServiceBase
{
private HubConnection hubConnection;
private IHubProxy priceProxy;
private Timer timer = new Timer();
private bool hasSubscribers = false;
public WinSer45()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer.Interval = 1000; // saniyede bir
timer.Elapsed += timer_Elapsed;
timer.Enabled = true;
hubConnection = new HubConnection("http://localhost:8080/signalr", useDefaultUrl: false);
priceProxy = hubConnection.CreateHubProxy("PriceHub");
hubConnection.Start().Wait();
priceProxy.On<bool>("UpdatePriceSubscriptionStatus", hasSubscribers =>
{
this.hasSubscribers = hasSubscribers;
});
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (hasSubscribers)
{
TestPrice testPrice = new TestPrice() { Id = 1, Buy = 1.2345, Sell = 9.8765, Symbol = "EURUSD" };
priceProxy.Invoke("NotifySubscribers", testPrice).Wait();
}
}
protected override void OnStop()
{
}
}
As you see I use the hasSubscribers flag to minimize the messages between hub and clients. And hasSubscribers flag is changed by SubscribeToPriceFeed and UnsubscribeFromPriceFeed methods.
If you look carefully you see the line below in SubscribeToPriceFeed:
context.Clients.All.updatePriceSubscriptionStatus(true);
I don't want to send the message to all clients but my client windows service. How can I store the connection Id of a specific client in my hub? If I can do that, I know I can send message to a specific connectionId as in the line below:
context.Clients.Client(connectionId).updatePriceSubscriptionStatus(true);
Thanks in advance,

pass source during connection
like this
hubConnection = new HubConnection("http://localhost:8080/signalr","source=windows",useDefaultUrl: false);
HUB
public override Task OnConnected()
{
var source= Context.QueryString['source'];
return base.OnConnected();
}
create a class which will hold the user with source
public class user {
public string ConnectionID {set;get;}
public string Source {set;get;}
}
declare a list in the hub
List<user> userList=new List<user>();
Then push the user during OnConnected
public override Task OnConnected()
{
var us=new user();
us.Source = Context.QueryString['source'];
us.ConnectionID=Context.ConnectionId;
userList.Add(us);
return base.OnConnected();
}
and during broadcast just filter it by source
var windowsUser=userList.Where(o=>o.Source == "windows").ToList(); // you'll get the windows user list

Related

How to use PendingIntent instead of an Intent with ConnectivityManager.ConnectivityAction with a callback in C# Xamarin API 28?

I need to make a simple callback in Xamarin, to check if the network status is connected or disconnected.
I have so far been doing it with this code:
class NetworkControl : INetworkControl
{
private readonly INetworkControl.ICallback _callback;
private readonly Context _context;
private readonly NetworkBroadcastReceiver _receiver = new NetworkBroadcastReceiver();
public NetworkControl(INetworkControl.ICallback callback, Context context)
{
_callback = callback;
_context = context;
IntentFilter filter = new IntentFilter(ConnectivityManager.ConnectivityAction);
context.RegisterReceiver(_receiver, filter);
}
public INetworkControl.ICallback Callback => _callback;
public INetworkControl.NetworkStatus Status
{
get
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
return INetworkControl.NetworkStatus.Connected;
}
return INetworkControl.NetworkStatus.Disconnected;
}
}
}
class NetworkBroadcastReceiver : BroadcastReceiver
{
private static String TAG = "NetworkBroadcastReceiver";
public override void OnReceive(Context context, Intent intent)
{
if (ShellBridge.Instance != null)
{
if (intent.Action.Equals(ConnectivityManager.ConnectivityAction))
{
NetworkInfo ni = (NetworkInfo)intent.Extras.Get(ConnectivityManager.ExtraNetworkInfo);
if (ni.isConnected)
{
// do something if connected
ShellBridge.Instance.NetworkBridge.Callback.NetworkStatusChanged(INetworkControl.NetworkStatus.Connected);
} else
{
ShellBridge.Instance.NetworkBridge.Callback.NetworkStatusChanged(INetworkControl.NetworkStatus.Connected);
}
}
}
}
The problem is, the function ConnectivityManager.ConnectivityAction in the Intent creating is depricated, and will soon be obsolete. After searching, I found that the pendingIntent should be used for that, but I could not find any valid example of how to use it.
The closest to what I need is this:
https://stackoverflow.com/questions/58588132/how-to-use-registernetworkcallback-with-pendingintent
But, it has not all the information I need.
I need it to be all programmatically, without changing the manifest, for, my app should be a fore- and background app.
Please help, and thank you for your time.
You can take a look at NetworkCallback .
public class ConnectionStateMonitor : NetworkCallback
{
NetworkRequest networkRequest;
public ConnectionStateMonitor()
{
networkRequest = new NetworkRequest.Builder().
AddTransportType(TransportType.Cellular).
AddTransportType(TransportType.Wifi).Build();
}
public void enable(Context context) {
ConnectivityManager connectivityManager = context.GetSystemService(Context.ConnectivityService) as ConnectivityManager;
connectivityManager.RegisterNetworkCallback(networkRequest, this);
}
public override void OnAvailable(Network network)
{
//network available
}
public override void OnLost(Network network)
{
//network lost
}
}
Usage
You just need to instantiate the class ConnectionStateMonitor and enable it , you could detect the network status with the method OnAvailable and OnLost .
ConnectionStateMonitor m = new ConnectionStateMonitor ();
m.enable(context);
Refer
https://github.com/xamarin/Essentials/issues/512
ConnectivityManager.CONNECTIVITY_ACTION deprecated
You don't need to reinvent the wheel. You can achieve all that with Xamarin Essentials' Connectivity.
Besides checking if there is a connectivity like this:
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
// Connection to internet is available
}
you can also track when the connectivity type changes:
public class ConnectivityTest
{
public ConnectivityTest()
{
// Register for connectivity changes, be sure to unsubscribe when finished
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
var access = e.NetworkAccess;
var profiles = e.ConnectionProfiles;
}
}

Hubproxy.On not firing after a long time

I have a wpf application which use SignalR HubConnection. everything works as expected. But after a long time (for example 10 hours) hubproxy.on method not firing, server sending data to client correctly also hubproxy.Invoke works correctly, only hubproxy.On does not work. There is no connection error or disconnected state. It happens on Production environment
Here is my client code
public partial class MainWindow : Window
{
private IHubProxy hubProxy;
private HubConnection connection;
private string url = "http://localhost:8080/signalr";
public MainWindow()
{
InitializeConnection();
InitializeComponent();
}
private void InitializeConnection()
{
connection = new HubConnection(url);
hubProxy = connection.CreateHubProxy("SignalRHub");
connection.StateChanged += connection_StateChanged;
hubProxy.On<string>("OnMessage", OnMMessage);
connection.Start();
}
void connection_StateChanged(StateChange obj)
{
if (obj.NewState == ConnectionState.Disconnected)
{
InitializeConnection();
}
}
private void OnMMessage(string message)
{
//This not works sometimes
MessageBox.Show(message);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//this works always, is being logged on the server
hubProxy.Invoke("SendMessage2", new object[1] { "dfdfdf" });
}
}
this is server side
public class SignalRHub : Hub
{
public Logger logger = new Logger();
public void Subscribe()
{
SubscriberCache.Subscribers.Add(Context.ConnectionId);
}
public void UnSubscribe()
{
if (SubscriberCache.Subscribers.Contains(Context.ConnectionId))
SubscriberCache.Subscribers.Remove(Context.ConnectionId);
}
public override Task OnDisconnected(bool stopCalled)
{
UnSubscribe();
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
if (!SubscriberCache.Subscribers.Contains(Context.ConnectionId))
SubscriberCache.Subscribers.Add(Context.ConnectionId);
return base.OnReconnected();
}
public void SendMessage(string message)
{
//this works always
logger.Info("message sending to client");
Clients.All.OnMessage(message);
}
}

How to instantiate a new Windows Form with Quartz.NET without problems?

I'm using the Quartz.NET library to create a job in my C# application.
I have some registers in my database, so I have a table wich contains a column called "start_date". The job runs every 50 seconds, so I compare the dates from the column "start_date" with the date of my computer, and if the dates are equal, I want to instantiate a new Windows Form with a message and a button.
At the moment, the new Windows Form is opening at the right moment, but the message is not showed and the window stops to respond.
Basically, in my code I have something like this:
FormMessage.cs
public partial class FormMessage : Form
{
public FormMessage()
{
InitializeComponent();
}
public FormMessage(double minutes)
{
InitializeComponent();
string message = string.Format("You have {0} minutes!", minutes);
lblMessage.Text = message ;
}
private void btnOK_Click(object sender, EventArgs e)
{
this.Close();
}
}
JobMessage.cs
public class JobMessage: IJob
{
List<Information> informations;
public void Execute(IJobExecutionContext context)
{
//Class with methods to get registers from database.
InformationAPI infoAPI = new InformationAPI();
informations = infoAPI.GetInformations();
foreach (Information info in informations)
{
DateTime computerDateTime = DateTime.Now;
DateTime infoDateTime = info.StartDate;
double difference;
if (DateTime.Compare(computerDateTime, infoDateTime) < 0)
{
difference = Math.Round(infoDateTime.Subtract(computerDateTime).TotalMinutes);
if (difference == 5)
{
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
}
}
}
}
Someone have some idea of the reason why the FormMessage window stops to respond?
Thank you for your attention!
You can try Quartz Listeners to let them open the form to show the data and keep the execution out of the job scope:
Action<IJobExecutionContext, JobExecutionException> listenerAction = (c, e) => {
var dataMap = context.GetJobDetail().GetJobDataMap();
var difference = dataMap.GetIntValue("difference");
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
var listener = new SyncJobListener(listenerAction);
And add the listener in to the scheduler:
scheduler.ListenerManager.AddJobListener(listener,
GroupMatcher<JobKey>.GroupEquals("GroupName"));
Using this SyncJobListener:
public class SyncJobListener : IJobListener
{
private readonly Action<IJobExecutionContext, JobExecutionException> _syncExecuted;
public string Name { get; private set; }
public SyncJobListener(
Action<IJobExecutionContext, JobExecutionException> syncExecuted
)
{
Name = Guid.NewGuid().ToString();
_syncExecuted = syncExecuted;
}
public void JobToBeExecuted(IJobExecutionContext context)
{
}
public void JobExecutionVetoed(IJobExecutionContext context)
{
}
public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
{
_syncExecuted(context, jobException);
}
}
I have not tested this so if the dataMap does not have any data, you are going to need to allow the persistance:
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class JobMessage: IJob {}

Validation Exception Queue using MSMQ in C#

I'm new in Microsoft Message Queue in Windows Server, I need to push, if the EmployeeID is NULL.
The Employee Model Class is
public class Employee
{
public string EmployeeID { get; set; }
public string EmployeeName { get; set; }
}
public void ValidationProcess(Employee emp)
{
if((emp != null) || (emp.EmployeeID == null))
{
// Push into Validation Exception Queue using MSMQ
}
}
Once the Data pushed into that Validation Exception Queue, it should be processed by separate process. Every 1hr the process need to initiate and it should call the following method
public void ValidationExceptionProcess(object obj)
{
// Some Inner Process
// Log the Error
}
Kindly guide me how to create and process it.
First Step:
Install MSMQs as a windows feature on the server/pc
Then:
- Create the queue if it does not exist
- Push the message in the queue asynchronously
Useful guide
Code example for pushing and retrieving messages from msmq:
public class ExceptionMSMQ
{
private static readonly string description = "Example description";
private static readonly string path = #".\Private$\myqueue";
private static MessageQueue exceptionQueue;
public static MessageQueue ExceptionQueue
{
get
{
if (exceptionQueue == null)
{
try
{
if (MessageQueue.Exists(path))
{
exceptionQueue = new MessageQueue(path);
exceptionQueue.Label = description;
}
else
{
MessageQueue.Create(path);
exceptionQueue = new MessageQueue(path);
exceptionQueue.Label = description;
}
}
catch
{
throw;
}
finally
{
exceptionQueue.Dispose();
}
}
return exceptionQueue;
}
}
public static void PushMessage(string message)
{
ExceptionQueue.Send(message);
}
private static List<string> RetrieveMessages()
{
List<string> messages = new List<string>();
using (ExceptionQueue)
{
System.Messaging.Message[] queueMessages = ExceptionQueue.GetAllMessages();
foreach (System.Messaging.Message message in queueMessages)
{
message.Formatter = new XmlMessageFormatter(
new String[] { "System.String, mscorlib" });
string msg = message.Body.ToString();
messages.Add(msg);
}
}
return messages;
}
public static void Main(string[] args)
{
ExceptionMSMQ.PushMessage("my exception string");
}
}
An other widely used way to do that would also be to use out of the box loggers which already contains this functionality like Enterprise Library or NLog which provide easy interfaces to do that.
For retrieving messages I would recommend a separate windows service which would periodically read messages and process them. An good example on how to do that is given here: Windows service with timer
Update: Windows Service Example:
MSMQConsumerService.cs
public partial class MSMQConsumerService : ServiceBase
{
private System.Timers.Timer timer;
public MSMQConsumerService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.timer = new System.Timers.Timer(30000D); // 30000 milliseconds = 30 seconds
this.timer.AutoReset = true;
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ProcessQueueMessages);
this.timer.Start();
}
protected override void OnStop()
{
this.timer.Stop();
this.timer = null;
}
private void ProcessQueueMessages(object sender, System.Timers.ElapsedEventArgs e)
{
MessageProcessor.StartProcessing();
}
}
and the MessageProcessor.cs
public class MessageProcessor
{
public static void StartProcessing()
{
List<string> messages = ExceptionMSMQ.RetrieveMessages();
foreach(string message in messages)
{
//write message in database
}
}
}

WCF NamedPipes Service and WPF Client

I'm working with a windows service where i want to add a GUI too it. I made a proof of concept with my service creating a ServiceHost object and hosting the WCF Named pipes service and then i Console application to consume the WCF service and also getting callback responses from the service (messages sent from server to connected clients). This works great my console application runs and get responses from the service with no interruption or delays.
However when doing the same thing in my WPF GUI application when clicking a button that then calls the WCF service it freezes the whole UI thread and then after a couple of minutes throws an exception and then the UI is updated with the message callback (server sends message to connected clients) but any return values from service is lost since the exception was thrown.
The two exception messages i have gothen is theses (the most common is the first):
1: The requesting action sent to net.pipe :/ / localhost / PipeGUI did not receive a response within the specified timeout (00:00:59.9989999). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Raise the deadline for action (by entering the channel / proxy to Icon Text Channel and set the property Operation Timeout) and verify that the service can connect to the client.
2: Communication object System.ServiceModel.Channels.ServiceChannel, can not be used for communication because it has been canceled.
Anyone got any ideas why this is happeing ?
I can post more code if neccessary.
UPDATE , added code for reference
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void MessageRecived(string message);
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]
public interface IPipeServiceContract
{
[OperationContract]
string Hello();
[OperationContract]
void Message(string msg);
[OperationContract(IsInitiating = true)]
void Connect();
[OperationContract(IsTerminating = true)]
void Disconnect();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, UseSynchronizationContext = false)]
public class PipeService : IPipeServiceContract
{
List<IClientCallback> _clients = new List<IClientCallback>();
public string Hello()
{
PublishMessage("Hello World.");
return "Return from method!";
}
public void Connect()
{
_clients.Add(OperationContext.Current.GetCallbackChannel<IClientCallback>());
}
public void Disconnect()
{
IClientCallback callback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
_clients.Remove(callback);
}
void PublishMessage(string message)
{
for (int i = _clients.Count - 1; i > 0; i--)
{
try
{
_clients[i].MessageRecived(message);
}
catch (CommunicationObjectAbortedException coae)
{
_clients.RemoveAt(i);
}
catch(CommunicationObjectFaultedException cofe)
{
_clients.RemoveAt(i);
}
}
}
public void Message(string msg)
{
PublishMessage(msg);
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged, IClientCallback
{
public ServiceController Service { get; set; }
protected IPipeServiceContract Proxy { get; set; }
protected DuplexChannelFactory<IPipeServiceContract> PipeFactory { get; set; }
public ObservableCollection<ServerActivityNotification> Activity { get; set; }
public override void BeginInit()
{
base.BeginInit();
PipeFactory = new DuplexChannelFactory<IPipeServiceContract>(this, new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/PipeGUI"));
}
public MainWindow()
{
InitializeComponent();
Activity = new ObservableCollection<ServerActivityNotification>();
Service = ServiceController.GetServices().First(x => x.ServiceName == "Server Service");
NotifyPropertyChanged("Service");
var timer = new DispatcherTimer();
timer.Tick += new EventHandler(OnUpdate);
timer.Interval = new TimeSpan(0, 0, 0, 0, 850);
timer.Start();
if (Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
void OnUpdate(object sender, EventArgs e)
{
Service.Refresh();
NotifyPropertyChanged("Service");
StartButton.IsEnabled = Service.Status != ServiceControllerStatus.Running ? true : false;
StopButton.IsEnabled = Service.Status != ServiceControllerStatus.Stopped ? true : false;
if (PipeFactory != null && Service.Status == ServiceControllerStatus.Running)
{
Proxy = PipeFactory.CreateChannel();
Proxy.Connect();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private void OnStart(object sender, RoutedEventArgs e)
{
try
{
Service.Start();
}
catch
{
Service.Refresh();
}
}
private void OnStop(object sender, RoutedEventArgs e)
{
try
{
if (Proxy != null)
{
Proxy.Disconnect();
PipeFactory.Close();
}
Service.Stop();
}
catch
{
Service.Refresh();
}
}
public void MessageRecived(string message)
{
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
ServerActivityNotification log = new ServerActivityNotification { Activity = message, Occured = DateTime.Now };
Activity.Add(log);
ListBoxLog.ScrollIntoView(log);
NotifyPropertyChanged("Activity");
}));
}
private void OnHello(object sender, RoutedEventArgs e)
{
try
{
Proxy.Message(txtSendMessage.Text);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
}
Try setting the UseSynchronizationContext property of the service behaviour to false:
[ServiceBehavior(UseSynchronizationContext = false)]
class MyService
{
}
[ServiceContract]
public interface IMyService
{
}
I believe that by default this is set to true, so you are currently attempting to consume and run the WCF service on the same thread resulting in a deadlock.
In any case, it sounds like you are trying to consume the WCF service on the UI thread of the WPF application. Generally it is recommended that you perform potentially long running tasks on the background thread as this keeps the interface responsive even if your service call takes a few seconds/minutes.
EDIT:
I tried and succeeded in replicating your problem. Trying to call the service on the UI thread results in the UI freezing. However, when I changed my code to call the service on a background task (see below), I was able to call the service and receive the callback:
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
var myService = DuplexChannelFactory<IMyService>.CreateChannel(new CallbackImplementation(),
new WSDualHttpBinding(),
new EndpointAddress(
#"http://localhost:4653/myservice"));
myService.CallService();
string s = "";
});
}
I have to confess, I am not entirely sure why this is so, and any clarification on exactly how WCF manages the thread hosting the service instance would be great in working out why this works.

Categories

Resources