How to automate task of consuming webservice - c#

I have a winform application which needs to consume a web service. Web service checks in the database for any changes. If there are any changes in the database the winform application should be notified and will perform some tasks accordingly. How do I do that?
I thought of using timer in my winform application and after say every 5 min connect to a web service and check if new changes are been done in Database. Is there any other way for this?
Update:
I am posting the code here base on the answers:
class PerformTasks
{
public static bool checkIfInProgress { get; set; }
public static void InitializeWebService()
{
try
{
Timer = new System.Timers.Timer(2000);
Timer.Elapsed += OnTimedEvent;
Timer.Enabled = true;
}
}
private static void callService()
{
using (var service = new WebServiceSoapClient())
{
checkIfInProgress = true;
task1();
task2();
popMessage();
checkIfInProgress = false;
}
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
if (checkIfInProgress == false)
{
callService();
}
}
private static void PpopMessage()
{
var form = new Form
{
StartPosition = FormStartPosition.Manual,
ShowInTaskbar = false,
Text = "Message",
Size = new Size(500, 200),
};
var screen = Screen.FromPoint(form.Location);
Label lblText = new Label();
lblText.Text ="Test Text";
lblText.Dock = DockStyle.Fill;
lblText.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
form.MaximizeBox = false;
form.Controls.Add(lblText);
form.Location = new Point(screen.WorkingArea.Right - form.Width, screen.WorkingArea.Bottom - form.Height);
form.Show();
}
Now everything works fine except 1 task i.e popMessage(code snippet on top). Here the form is opened but it appears to be loading always. Before using times it used to work fine. How can I handle it?

That's the only way especially if the web service is not WCF-based or if you can't afford to modify it.
If you're using a timer just make sure you use System.Timers.Timer and follow the instructions here so that the Elapsed handler is executed on the UI thread. Moreover, when the timer ticks you should probably spawn a worker thread (or Task, or await on an async method) that makes the service call. You don't want your UI to be blocked while the service call is in progress.
If you have control over the web service, then you may want to explore WCF Duplex Services, which allow you to callback clients from within services.

SignalR allows you to implement Web Services to do this very thing, in real time (no need for a timer or delay between updates). It allows you to set up a persistent connection between client and server, the server is then able to send messages to the client at any point using a stack of transports; WebSockets, Server Sent Events, Forever Frame and Long Polling) based on support available in that order.
You could use SignalR to establish the connection and when something happens on the Server (such as the change in the database you mentioned) broadcast to all clients that need to be notified. eg.
Clients.All.notifyDatabaseChanged(args);

Related

Blazor Server App: How to design so that the client requests receive the same data

Let's suppose we have this simple classical timer in default Blazor Server.
<p>Current count: #currentCount</p>
<button class="btn btn-primary" #onclick="IncrementCount">Click me</button>
#code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
Console.WriteLine($"Count incremented: {currentCount}");
}
private Timer timer;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
timer = new Timer();
timer.Interval = 1000;
timer.Elapsed += OnTimerInterval;
timer.AutoReset = true;
// Start the timer
timer.Enabled = true;
}
base.OnAfterRender(firstRender);
}
private void OnTimerInterval(object sender, ElapsedEventArgs e)
{
IncrementCount();
InvokeAsync(() => StateHasChanged());
}
public void Dispose()
{
// During prerender, this component is rendered without calling OnAfterRender and then immediately disposed
// this mean timer will be null so we have to check for null or use the Null-conditional operator ?
timer?.Dispose();
}
}
Right now when I open this page in the browser time starts from zero for every new window(as expected). However, I want to open multiple pages in the browser and every page should see the same time (if the time shows 10 in my first window and I open a new window I want the time in both to be the same). How can I achieve that?
In general, I want to create a simple SPA that shows the real-time stock price values to the users.
Any suggestions would be appreciated.
SignalR would probably be the preferred Microsoft solution for this problem, since it provides real-time web functionality to apps.
At a high level, you would need to:
Create a Hub on the server that would be responsible for handling the connection with clients and broadcasting messages (in your case stock information).
Assuming you already have some sort of service for polling/receiving stock updates, that service could use the IHubContext to publish updates to the clients through the Hub.
The client would need a HubConnection to receive messages from the server.
Here's a full example of a Chat app using a similar approach: https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-tutorial-build-blazor-server-chat-app
Since I couldn't find a good example of an application using these components similar to your use case: I put together a simple repo that can be found here: https://github.com/t-j-c/Stack.Blazor.SignalR
Just make currentCount static.
private static int currentCount = 0;
It should work.

Background Worker and Timer in System Tray App C#

This is an incredibly simple task tray app - using ApplicationContext and a few guides I found online.
The purpose of the app is to query a small REST API and show a message box to the user on a given result. I need to essentially have the API query in a background loop, running every 10 seconds or something similar. This is to report on data that I've made accessible via another service.
I've done some reading and it seems a BackgroundWorker and Timer is an appropriate option, but I'm lost on where to go next. How exactly can I achieve this? I initially tried adding a while(true) loop to the TaskTrayApplicationContext but it just created an infinite loop whereby you couldn't do anything else with the app.
namespace TaskTrayApplication
{
public class TaskTrayApplicationContext : ApplicationContext
{
NotifyIcon notifyIcon = new NotifyIcon();
Configuration configWindow = new Configuration();
public TaskTrayApplicationContext()
{
MenuItem configMenuItem = new MenuItem("Configuration", new EventHandler(ShowConfig));
MenuItem exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
notifyIcon.Icon = TaskTrayApplication.Properties.Resources.AppIcon;
notifyIcon.DoubleClick += new EventHandler(ShowMessage);
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { configMenuItem, exitMenuItem });
notifyIcon.Visible = true;
}
void ShowMessage(object sender, EventArgs e)
{
// Only show the message if the settings say we can.
if (TaskTrayApplication.Properties.Settings.Default.ShowMessage)
MessageBox.Show("This is the Serenity TaskTray Agent.");
}
void ShowConfig(object sender, EventArgs e)
{
// If we are already showing the window meerly focus it.
if (configWindow.Visible)
configWindow.Focus();
else
configWindow.ShowDialog();
}
void Exit(object sender, EventArgs e)
{
// We must manually tidy up and remove the icon before we exit.
// Otherwise it will be left behind until the user mouses over.
notifyIcon.Visible = false;
Application.Exit();
}
}
}
And the Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TaskTrayApplication
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Instead of running a form, we run an ApplicationContext.
Application.Run(new TaskTrayApplicationContext());
}
}
}
Threading is hard, concurrency is hard. Background worker and System.Timers are both constructs that run in their own thread.
winforms won't allow for interaction between threads that own a control (read: that created a control) and threads that don't. This is a whole subject apart i wont get into now - theres good stuff to read out there why this is and how to go about it: https://visualstudiomagazine.com/articles/2010/11/18/multithreading-in-winforms.aspx
There are tools to help, one is the dispatchertimer:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=netcore-3.1
This is a special timer that instead of its own thread, schedules tasks on the main thread. The main thread in a winforms application handles the drawing of controls, showing of the different windows etc. e.g. this 'owns' all controls.
A sample can be seen on msdn, i adopted it here to show you what you could do:
public class TaskTrayApplicationContext : ApplicationContext
{
...
DispatcherTimer dispatcherTimer;
public TaskTrayApplicationContext()
{
...
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
// Fetch your data via a rest api
var myData = MyDataFunction();
// check and show dialog if the data is not okay
if(myData.Result.Value = 'NOT_OKAY!')
ShowMessage(this, myData.Result); // or something.
}
...
Now since this does not utilize a second thread, this means the main ui thread could be blocked from drawing the windows, reacting to user input etc. because its busy doing work in the timer_tick function. This would for example happen if your rest call takes a long time.
This will make your application freeze and irresponsive. This could be a problem but most likely wont, so lets burn that bridge when we get to it.

C# my application starts in task manager but not shown in Desktop

I created a windows service with Top Shelf. This service tests the battery level and when battery successfully charged(80%) it opens any UI Windows Form or application to nodify me about that (In this example it opens .txt). When i test it in Visual Studio in works perfectly so my UI or any application apperas both in Task Manager and Desktop but when i install it as service, UI or any application appears only in Task Manger not in Desktop here is code:
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Timers;
using System.Windows.Forms;
namespace SimpleHeartBeatService
{
public class Heartbeat
{
private readonly System.Timers.Timer _timer; //referance yaratdiq
private static bool firstChecker = true;
private static bool secondChecker = true;
public Heartbeat()
{
_timer = new System.Timers.Timer(1000) //referanca objecr menimsetdik
{
AutoReset = true //Object initializer (kitabda Constr deeper de en axirda var)
};
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{ //Bize lazim olan hisse burdan asagidadir
PowerStatus status = SystemInformation.PowerStatus;
if (status.PowerLineStatus.ToString() == "Online" && secondChecker)
{
firstChecker = true;
secondChecker = false;
}
if (firstChecker && status.PowerLineStatus.ToString() == "Online" &&
status.BatteryLifePercent * 100 > 80)
{
Process.Start(#"C:\Users\qarib\Desktop\Test\batteryLevel.txt");
firstChecker = false;
}
if (status.PowerLineStatus.ToString() == "Offline")
{
secondChecker = true;
}
// code here
Thread.Sleep(500);
}
public void Start() //Bunlar mutleq yazilmalidir
{
_timer.Start();
}
public void Stop() //Bunlar mutleq yazilmalidir
{
_timer.Stop();
}
}
}
http://securityinternals.blogspot.com/2014/02/windows-session-0-isolation.html
You can read the details in the article, but suffice it to say that Windows services cannot interact with the desktop of regular users anymore. My guess is that your form is actually opening in Session 0, which is not something you'll normally be able to view or interact with.
In order to get around this problem, here's what my project has done.
We have created a Windows service that exposes some mechanism for communicating with it. In our case, the Windows service originally exposed a WCF-based interface using both sockets and pipes. Because of the overhead, we eventually moved to a TCP socket over localhost, and today we are using shared memory. The mechanism doesn't really matter for the purpose of this discussion. Your service just has to provide some way for a user-run application to connect, communicate, and disconnect.
Once this is in place, you can build an application that provides a UI for communicating with the service. We've done this and allowed the application to be minimized to the system tray to give the appearance that it's a behind-the-scenes thing. The system tray icon then provides a popup menu that allows the user to send commands directly to the service or open the application for additional features.
HTH

Locking main() thread

Not exactly sure on the terminology here but here I go basically I have the main() thread of my application that starts and calls two threads, one sets up an event handler to wait for specific registry keys to change, while the other starts a timer to write any changes made to an xml file every 5 mins or so and runs continuously. The issue I have is that once the two methods called are initialized it goes back to main and ends the program. My relevant code sections can be found below, so any help would be appreciated:
static void Main(string[] args)
{
runner one = new runner();
runner two = new runner();
Thread thread1 = new Thread(new ThreadStart(one.TimerMeth));
Thread thread2 = new Thread(new ThreadStart(two.start));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
public void TimerMeth()
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 300000;
timer.Enabled = true;
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
file write = new file();
write.write(RegKeys);
}
public void start()
{
if (File.Exists("C:\\test.xml"))
{
file load = new file();
RegKeys = load.read(RegKeys);
}
string hiveid = "HKEY_USERS";
WindowsIdentity identity = WindowsIdentity.GetCurrent();
string id = identity.User.ToString();
string key1 = id + "\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Windows Messaging Subsystem\\\\Profiles\\\\Outlook\\\\0a0d020000000000c000000000000046";
List<string> value1 = new List<String> { "01020402", "test" };
valuechange = new RegistryValueChange(hiveid, key1, value1);
valuechange.RegistryValueChanged += new EventHandler<RegistryValueChangedEventArgs>(valuechange_RegistryValueChanged);
try
{
valuechange.Start();
}
catch
{
StreamWriter ut;
ut = File.AppendText("C:\\test.txt");
ut.WriteLine("error occured in starting management");
ut.Close();
}
file test = new file();
test.checkfile("C:\\test.xml");
}
void valuechange_RegistryValueChanged(object sender, RegistryValueChangedEventArgs e)
{
// deals with the returned values
}
Basically all the code works fine I've been testing it in a windows form application but now I need to run it in a standalone app with no interface in the background and need it to keep writing to the xml file and the change event to stay alive.
As you can expect, the Main() method is terminating because execution flows out of the Join() methods back to the main thread, and then terminates.
Either place loops in the methods TimerMeth() and start(), or more appropriately redesign the application into a Windows Service (as zac says).
You have a couple of issues.
Your first thread is simply creating a timer (which launches another thread). This thread is terminating very quickly, making your call to Join rather meaningless. What this thread should be doing is actually doing the waiting and the checking. You can easily adapt your code like this:
public void TimerMeth()
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 300000;
timer.Enabled = true;
try
{
while(true)
{
OnElapsedTime(null, null); // you should change the signature
Thread.Sleep(30000);
}
}
catch(ThreadAbortException)
{
OnElapsedTime(null, null);
throw;
}
}
Obviously you should change the signature of OnElapsedTime to eliminate the parameters, since they aren't used here.
I have a feeling that something is amiss in the way that the file handling is being done, but given that I don't understand exactly what that code does, I'm going to refrain from commenting. What, exactly, is the purpose of the file? Where is RegKeys defined?
Try building this into a windows service.
This thread contains two suggestions for finding the logged on user from a windows service, but I am not sure if they work.
Your methods will run once, then the thread will exit. There is nothing to keep them running.
Try this:
thread1.IsBackground = true;
thread2.IsBackground = true;
public void start()
{
while(true)
{
// ... do stuff
Thread.Sleep(1000*60*5) // sleep for 5 minutes
}
}
public void TimerMeth()
{
while(true)
{
file write = new file();
write.write(RegKeys);
Thread.Sleep(30000);
}
}
As other posters have noted, you will also then need to ensure your main method doesn't exit. Making the application a windows service seems like a good way to solve this in your case.
You might also want to handle ThreadInterruptedException and ThreadAbortException on your threads.
And if you really want to get into the nitty gritty of threading, check out this Free C# Threading E-Book by Joe Albahari.
To keep the main thread alive, one of the simplest ways is to add the following line to the end of your main function:
Thread.Sleep(Timeout.Infinite);
The Thread will terminate when your ThreadStart function returns, which allows the main thread to continue after Join(). Since you are just setting up a timer to fire off, the method will return very quickly. You need to provide a lock of some sort to keep your application from exiting.
Also, it doesn't look like you need to use threads at all to do what you are trying. Instead, just use the Timer and provide the lock to keep your Main() from terminating.
It looks to me like all of your functions are completing? ie, they all "fall out the bottom". Once all the functions have run through there is nothing left to do and your app will close. You want to run a loop of some sort in main.
You will also need to take a look at your timer. I suspect it is currently being garbage collected. You create it in the scope of your function but that function is being left so there is no longer a reference to your timer and it will be collected. Your timer needs to be a root.

Wrapping an asynchronous method synchronously in C#

I have a third party library containing a class which performs a function asynchronously. The class inherits from the Form. The function basically performs a calculation based on data stored in a database. Once it has finished, it calls a _Complete event in the calling form.
What I would like to do is call the function synchronously but from a non-windows form application. The problem is, no matter what I do, my application blocks and the _Complete event handler never fires. From a windows form I can simulate the function running synchronously by using a "complete" flag and a "while (!complete) application.doevents", but obviously application.doevents isnt available in a non-windows form application.
Is there something that would stop me using the class's method outside of a windows form application (due to it inheriting from 'Form') ?
Is there some way I can work around this ?
Thanks,
Mike
At a stab it might be worth trying something like the following which uses a WaitHandle to block the current thread rather than spinning and checking a flag.
using System;
using System.Threading;
class Program
{
AutoResetEvent _autoEvent;
static void Main()
{
Program p = new Program();
p.RunWidget();
}
public Program()
{
_autoEvent = new AutoResetEvent(false);
}
public void RunWidget()
{
ThirdParty widget = new ThirdParty();
widget.Completed += new EventHandler(this.Widget_Completed);
widget.DoWork();
// Waits for signal that work is done
_autoEvent.WaitOne();
}
// Assumes that some kind of args are passed by the event
public void Widget_Completed(object sender, EventArgs e)
{
_autoEvent.Set();
}
}
I've got some more information on this problem (I'm working in the same team as mikecamimo).
The problem also occurs in the Windows Forms application, when replicated correctly. In the original OP, the problem didn't occur in the windows form because there was no blocking. When blocking is introduced by using a ResetEvent, the same problem occurs.
This is because the event handler (Widget_Completed) is on the same thread as the method calling Widget.DoWork. The result that AutoResetEvent.WaitOne(); blocks forever because the event handler is never called to Set the event.
In a windows forms environment this can worked around by using Application.DoEvents to poll the message queue and allow the event the be handled. See below.
using System;
using System.Threading;
using System.Windows.Forms;
class Program
{
EventArgs data;
static void Main()
{
Program p = new Program();
p.RunWidget();
}
public Program()
{
_autoEvent = new AutoResetEvent(false);
}
public void RunWidget()
{
ThirdParty widget = new ThirdParty();
widget.Completed += new EventHandler(this.Widget_Completed);
data = null;
widget.DoWork();
while (data == null);
Application.DoEvents();
// do stuff with the results of DoWork that are contained in EventArgs.
}
// Assumes that some kind of args are passed by the event
public void Widget_Completed(object sender, EventArgs e)
{
data = e;
}
}
In a non windows forms application, such as a Windows Service, Application is not available so DoEvents cannot be called.
The problem is one of threading and that widget.DoWork's associated event handler somehow needs to be on another thread. This should prevent AutoResetEvent.WaitOne from blocking indefinitely. I think... :)
Any ideas on how to accomplish this would be fantastic.
AutoResetEvent _autoEvent = new AutoResetEvent(false);
public WebBrowser SyncronNavigation(string url)
{
WebBrowser wb = null;
wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.ScriptErrorsSuppressed = true;
wb.Navigate(new Uri(url));
while (!_autoEvent.WaitOne(100))
Application.DoEvents();
return wb;
}
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//throw new NotImplementedException();
_autoEvent.Set();
}
Do you have the source for the component? It sounds like it's relying on the fact it will be called from a WinForms environment (must be a good reason why a library inherits from Form!), but it's hard to know for sure.

Categories