Backgroundworker and UI interactivity - c#

I am using a background worker for a time consuming operation ( To upload some data to server and download some data from server). All server communications are via http requests (GET,POST,PUT,Delete) and the code i used are
private void btnSync_Click(object sender, RoutedEventArgs e)
{
btnSync.IsEnabled = false;
BackgroundWorker syncWorker = new BackgroundWorker();
syncWorker.WorkerReportsProgress = true;
syncWorker.WorkerSupportsCancellation = true;
syncWorker.DoWork+=new DoWorkEventHandler(syncWorker_DoWork);
syncWorker.ProgressChanged+=new ProgressChangedEventHandler(syncWorker_ProgressChanged);
syncWorker.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(syncWorker_RunWorkerCompleted);
syncWorker.RunWorkerAsync();
}
private void syncWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker target = (BackgroundWorker)sender;
foreach (...........) // a loop to do some set of actions on some objects
{
target.ReportProgress(1, obj);
target.ReportProgress(2, obj);
target.ReportProgress(3, obj);
}
target.ReportProgress(4);
}
private void syncWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 1)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id); //Issue a GET request and read and process resposne (deserialize json data and insert to local DB)
}
if (e.ProgressPercentage == 2)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id);//Issue a GET request and read and process resposne(deserialize json data and insert to local DB)
}
if (e.ProgressPercentage == 3)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id);//Issue a GET request and read and process resposne (deserialize json data and insert/Update response data to local DB)
}
if (e.ProgressPercentage ==4)
{
mySyncHelper.DOPOSTToServer();//Issue a POST request and read and process resposne (deserialize json data and insert/Update response data to local DB)
mySyncHelper.DOPUTToServer();
mySyncHelper.DODELETEToServer();
}
}
private void syncWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
txtSyncStatus.Visibility = Visibility.Visible;
txtSyncStatus.Text = string.Concat(DbookLanguage.message_error, Environment.NewLine , e.Error.Message.ToString());
}
else
{
btnSync.IsEnabled = true;
txtSyncStatus.Text = "";
}
}
The problem i am facing is # some stages the application is waiting some time for getting response back for the http requests and during this state the entire application goes to freezed state . The code i used for getting response from server is
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
and on this state the application is non responding .
How can i prevent this ? Is any other good way to make series of Http requests and make app UI interactive

As suggested in comments, the code in "ProgressChanged" runs on UI thread which is causing your UI to freeze (because you have server request in this handler).
ProgressChanged
handler is useful (and should be used) only when you have some regular UI updates (like percentage completed). In your cases, consider moving your entire code to a new method (say GetFromServer(int id)) and make call to this method from inside DoWork. This will cause all this I/O work running on background thread.

Related

HttpClient throws httprequest exception on network restore

I am using System.Net.Http.HttpClient to make postaysnc request. While request is in progress I unplug the network cable, receive HttpRequestException.
After some time plug the network cable again and make the postasync request, getting the HttpRequestException - sometimes i get the response server not available,sometimes timeout
Do i need to dispose the httpclient on exception and recreate when the request is made? How to make the query successful on network restore.
private async Task<string> GetServerResult()
{
try
{
var response = await myHttpClient.PostAsync("https://google.com", httpContent);
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex)
{
throw new HttpRequestException(ex.Message, ex.InnerException);
}
}
As per your requirement, you have to change implement some sort of implementation in that case. My proposed solution is use to a caching mechanism at WCF Client and update it periodically.
The very simple implementation could be as: You have a very simple singleton class of and a periodic Timer fetches the data from your mentioned endpoint. It stores the last cached data so that you have a copy of the data and when the hits are failed you can configure a fallback mechanism for that. For instance you have an implementation like
//You single Cache class
public sealed class ClientCache
{
#region Singleton implementation
private static ClientCache _clientCache = new ClientCache();
private ClientCache()
{
}
public static ClientCache Instance => _clientCache;
#endregion
//Timer for syncing the data from Server
private Timer _timer;
//This data is the cached one
public string data = string.Empty;
internal void StartProcess()
{
//Initializing the timer
_timer = new Timer(TimeSpan.FromMinutes(1).TotalMilliseconds); //This timespan is configurable
//Assigning it an elapsed time event
_timer.Elapsed += async (e, args) => await SyncServerData(e, args);
//Starting the timer
_timer.Start();
}
//In this method you will request your server and fetch the latest copy of the data
//In case of failure you can maintain the history of the last disconnected server
private async Task ProcessingMethod(object sender, ElapsedEventArgs e)
{
//First we will stop the timer so that any other hit don't come in the mean while
timer.Stop();
//Call your api here
//Once the hit is completed or failed
//On Success you will be updating the Data object
//data = result from your api call
//Finally start the time again as
timer.Start();
}
}
Now coming to Step two where to initialize the ClientCache Class. The best options are to initialize it in Global.asax class
protected void Application_Start()
{
//As
ClientCache.Instance.StartProcess();
}
Now whenever your frontend calls the method you don't need to go back to the server. Just send back the result from your cache as:
private Task<string> GetServerResult()
{
return Task.FromResult(ClientCache.Instance.data);
}

asp.net and multi threading and C# object

I'm new to ASP.net programming and I don't really understand the thing about multi threading . When I debug in my ASP.net app/website it shows a tab that I'm not familiar with: Parallel watch, it's something about multi threading. When I search about multi threading I only find examples about how to use multi-threading etc. The thing is, I'm not using any class for threading.
What I got in my code:
is my main page:
Index.aspx.cs:
protected void Button1_Click(object sender, EventArgs e)
{
string Message = PLC1.Connect();
lblClock.Text = Message;
}
and
protected void Timer1_Tick(object sender, EventArgs e)
{
if(PLC1.x_ConnectionEstablished)
{
string Message;
Message = PLC1.ReadDB(1, 0, 17);
if (Message == "OK")
{
lblClock.Text = "Connected and retrieved value!!";
}
else
{
lblClock.Text = Message;
}
}
else
{
lblClock.Text = "No connection! Press the button!";
}
}
my class: PLC.cs:
public string Connect()
{
int Result;
string Message;
Result = s7_S7Client.ConnectTo(s_IP, i_Rack, i_Slot);
Message = s7_S7Client.ErrorText(Result);
x_ConnectionEstablished = s7_S7Client.Connected();
return Message;
}
public string ReadDB(int a_DBnr, int a_startPos, int a_size)
{
int Result;
string Message = "";
try
{
Result = s7_S7Client.DBRead(a_DBnr, a_startPos, a_size, bArray_Buffer);
Message = s7_S7Client.ErrorText(Result);
}
catch (Exception E)
{
Message = E.Message;
}
return Message;
}
When I press the button (while debugging) it jumps to the Connect() of my PLC class. And it actually gives true on the x_ConnectionEstablished. But when my Timer triggers it doesn't go into my if-statement. Since it's saying: x_ConnectionEstablished = false.
I can add the code to the timer, but I don't want to execute the connection method each time I get in the timer(that's what I'm using this x_connectionEstablished for.
So my question is how does this threading work and how can I get my website running decently with or without the threading?
Your PLC1 and s7_S7Client appear to create some kind of persistent connection, for example through TCP sockets or a serial connection.
You don't want to create connections like this from an HTTP back-end, because HTTP is (or at least supposed to be) stateless: each request starts with a clean slate, all your variables (and thus connections) from the previous request are gone.
So I would advise wrapping this logic into a Windows Service that manages connecting to the PLC, and exposing the logic from this service through WCF.
Then your web application can issue requests, through WCF, to the service, and the service in turn talks to the PLC and maintains the connection.

How to make windows service request wait until the previous request is complete

I am working on a window services application and my window service will call one of the web services in certain intervals (for example 3 min). From the web service I will get data from a database and using that data I will send an email.
If I am having huge sets of rows in my db table it will take some time to send the mail. Here I have the problem: The window services send the first request and it will handle some set of records. So, while processing it by the web service, the window service sends another request to the web service before it has completed the first request.
Due to this, the web service gets the same records from db again and again whenever it receives a new request from the windows service.
Can any one suggest me how to lock the previous request until it completes its work or some other way to handle this situation?
Web Service call:
protected override void OnStart(string[] args)
{
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 180000;
timer.AutoReset = false;
timer.Enabled = true;
}
Inside Method
using (MailWebService call = new MailWebService())
{
try
{
call.ServiceUrl = GetWebServiceUrl();
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
call.CheckMailQueue();
}
catch (Exception ex)
{
LogHelper.LogWriter(ex);
}
finally
{
}
}
The Monitor class works great for this scenario. Here is an example of how to use it:
// This is the object that we lock to control access
private static object _intervalSync = new object();
private void OnElapsedTime(object sender, ElapsedEventArgs e)
{
if (System.Threading.Monitor.TryEnter(_intervalSync))
{
try
{
// Your code here
}
finally
{
// Make sure Exit is always called
System.Threading.Monitor.Exit(_intervalSync);
}
}
else
{
//Previous interval is still in progress.
}
}
There is also an overload for TryEnter that allows you to specify timeout for entering the section.

Using DownloadDataAsync with Cookie aware Webclient to get Update Progress

I am using a Webclient with a CookieContainer to download data from a webservice (Sharepoint).
I want to DownloadDataAsync so that I download multiple documents and update a single Progress Bar for each Document as it downloads. The non async version - DownloadData does not send Progress updates.
How do I get the Async version to wait at the doc.BinaryData = xx line before moving on the next Document?
How do I get the byte array from the DownloadFileCompleted event?
How can apply the changes to the progressbar without using DoEvents?
partial class Form()
{
void Main()
{
List urls= new List();
//urls.Add("xxxxxxx"); //get them from somewhere
for (int i = 0; i < urls.Count; i++)
{
var doc = new Document();
doc.BinaryData = DocumentAsArray(urls.ElementAt(i));
entities.AddToDocument(doc);
}
}
public byte[] DocumentAsArray(string URL)
{
byte[] #return = null;
using (CookieAwareWebClient client = new CookieAwareWebClient())
{
client.CookieContainer = spAuthentication.CookieContainer;
// Assign the events to capture the progress percentage
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadDataAsync(new Uri(URL));
}
return #return;
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBarControlFile.Position = e.ProgressPercentage;
var newPc = string.Format("Downloaded {0} %", e.ProgressPercentage);
labelControlFile.Text = newPc;
Application.DoEvents();
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
progressBarControlFile.Position = progressBarControlFile.Properties.Maximum;
labelControlFile.Text = string.Format("{0} %", progressBarControlFile.Properties.Maximum);
Application.DoEvents();
}
}
public class CookieAwareWebClient : WebClient
{
public CookieContainer CookieContainer { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
var webRequest = request as HttpWebRequest;
if (webRequest != null)
{
webRequest.CookieContainer = this.CookieContainer;
webRequest.KeepAlive = false;
}
return request;
}
}
}
You have to wait for the DownloadFileCompleted event. You can either set a volatile bool on which you poll or do this with an event. There are only minor differences performance-wise, but generally probably events are the cleaner solution (I prefer bool-polling). You might in your case actually want to wait for this to happen at the end of DocumentAsArray.
http://msdn.microsoft.com/en-us/library/ms144190.aspx The downloaded data is available in the Result property.
I don't know the DoEvents methods - if I understand correctly what you want to do, you want to update your UI? You would have to call InvokeEx (at least that is the easiest and.. only way I know). Look at this great :) article http://www.andreas-reiff.de/2011/06/accessing-a-winformwpf-control-from-another-thread-through-invoke/ . Alternatively, stay at stackoverflow and look at Best Way to Invoke Any Cross-Threaded Code?.
So to recap, if you just want the async to get the progress-update, simply change your DocumentAsArray function to wait for the DownloadFileCompleted event as outlined in 1. Then all you have to do is to be careful that you do not call your UI code from another thread - you should get an exception in Debug telling you not to do so (it runs fine in Release - most of the time). So use the InvokeEx-call.

DownloadStringAsync() does not download the string asynchronously

Trying to implement downloadStringAsync() to prevent UI freezing for 10 seconds when downloading one byte of data. However, even though the download completes, it is freezing the UI just as if I used downloadString().
Here is my code:
public void loadHTML()
{
WebClient client = new WebClient();
// Specify that the DownloadStringCallback2 method gets called
// when the download completes.
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(loadHTMLCallback);
client.DownloadStringAsync(new Uri("http://www.example.com"));
return;
}
public void loadHTMLCallback(Object sender, DownloadStringCompletedEventArgs e)
{
// If the request was not canceled and did not throw
// an exception, display the resource.
if (!e.Cancelled && e.Error == null)
{
string result = (string)e.Result;
// Do cool stuff with result
}
}
Encountered the same problem, and found a solution.
Quite complex discussion here:
http://social.msdn.microsoft.com/Forums/en-US/a00dba00-5432-450b-9904-9d343c11888d/webclient-downloadstringasync-freeze-my-ui?forum=ncl
In short, the problem is web client is searching for proxy servers and hanging the app.
The following solution helps:
WebClient webClient = new WebClient();
webClient.Proxy = null;
... Do whatever else ...

Categories

Resources