I use WebClient in a Silverlight Appliction to access REST Services. Its an unknown amount of asynchronous calls.
The cool thing is, you can order your requests and responses! the responses are matched to theirs requests! This is necessary, because you do not know in what order the responses will come back.
But how do i get a "timeout" for my calls with WebClient? lets say 15 sec
I would like kinda to stick to WebClient/this code with delegates/lambda. I know there is a timeout property with WebRequest class, but i am not sure if just can replace WebClient with WebRequest but keep the functionality.
int maxRequests = list_S.Count;
// amount of URI
foreach (string item in list_S)
{
bool isValid = Uri.IsWellFormedUriString(item, UriKind.Absolute);
Uri uriTest;
if(isValid) //if it is valid Uri, send request
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += (s, args) =>
{
if (args.Error == null)
{
dict.Add((int)args.UserState, args.Result);
}
//here you test if it is the last request... if it is, you can
//order the list and use it as you want
if (dict.Count == maxRequests)
{
var orderedResults = dict.OrderBy(a => a.Key);
}
closeTabitem_SensorSource();
};
wc.DownloadStringAsync(new Uri(item), i++);
}
else
{
MessageBox.Show("Uri FAIL!: " + item);
}
}
The WebRequest does not provide a means for managing request timeouts either.
The approach you need to take is to use WebClient in conjunction with your own code based on a DispatcherTimer that will call the WebClient CancelAsync method.
Related
I'm new in TPL world, and I did that code:
var myItems = myWpfDataGrid.SelectedItems;
this.Dispatcher.BeginInvoke(new Action(() =>
{
var scheduler = new LimitedConcurrencyLevelTaskScheduler(5);
TaskFactory factory = new TaskFactory(scheduler);
foreach (MyItem item in myItems)
{
Task myTask = factory.StartNew(() =>
DoLoooongWork(item)
).ContinueWith((t) =>
{
Debug.WriteLine(t.Exception.Message);
if (t.Exception.InnerException != null)
{
Debug.WriteLine(t.Exception.InnerException.Message);
}
},
TaskContinuationOptions.OnlyOnFaulted);
}
}), null);
The only one access to gui is "var myItems = myWpfDataGrid.SelectedItems;"
and it is read only! The function "DoLoooongWork()" does access to serial ports, etc. It's a separated SDK function that doesn't access the GUI. I know that "Dispatcher.BeginInvoke" is a bit redundant, but I don't know what I can do, or what I'm doing wrong. The only reason to this code is to free the GUI while "DoLoooongWork()" executes, but the GUI is frozen!
What's wrong with that code?
edit
Thanks to #Euphoric help, I discovered the problem that is similar to that post:
COM Interop hang freezes entire COM system. How to cancel COM call
I presume some objects inside DoLoooongWork require thread affinity and message pumping. Try my ThreadWithAffinityContext and see if helps, use it like this:
private async void Button_Click(object sender, EventArgs e)
{
try
{
using (var staThread = new Noseratio.ThreadAffinity.ThreadWithAffinityContext(
staThread: true, pumpMessages: true))
{
foreach (MyItem item in myItems)
{
await staThread.Run(() =>
{
DoLoooongWork(item);
}, CancellationToken.None);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
More info about ThreadWithAffinityContext.
[UPDATE] You mentioned in the comments that the code inside DoLoooongWork looks like this:
zkemkeeper.CZKEM axCZKEM1 = new zkemkeeper.CZKEM();
axCZKEM1.Connect_Net(ip, port);
I never heard of "zkemkeeper" before, but I did a brief search and found this question. Apparently, Connect_Net only establishes the connection and starts a session, while the whole communication logic happens asynchronously via some events, as that question suggests:
bIsConnected = axCZKEM1.Connect_Net("192.168.0.77", Convert.ToInt32("4370"));
if (bIsConnected == true)
{
iMachineNumber = 1;
if (axCZKEM1.RegEvent(iMachineNumber, 65535))
{
this.axCZKEM1.OnFinger += new kemkeeper._IZKEMEvents_OnFingerEventHandler(axCZKEM1_OnFinger);
this.axCZKEM1.OnVerify += new zkemkeeper._IZKEMEvents_OnVerifyEventHandler(axCZKEM1_OnVerify);
// ...
}
}
That would be a whole different story. Leave a comment if that's the case and you're still interested in some solution.
I had a hunch that something working with serial port would try to use application's event loop to do it's work. So it actually bypasses the whole dispatcher and thread system and blocks the application. I'm not experienced in this field so I don't know how to solve it, but this is different question.
Just getting my head around WCF, so forgive me for the inelegant coding.
The issue I'm having is I seem to be submitting data twice to my service (see screenshot), even though (I think) I'm only doing it once.
Could someone please let me know what I might be doing wrong? Or even just suggest a better way to do it if I'm doing it inefficiently.
Code follows:
public void EndOfLevel()
{
GlobalVariable.TotalQuestionsAsked = 10;
GlobalVariable.CorrectDecimal = GlobalVariable.Correct / GlobalVariable.TotalQuestionsAsked;
//Show loading screen
UploadingScreen.Visibility = Visibility.Visible;
//Submit this levels results.
Service1Client client = null;
client = new Service1Client();
//Gather the results and details
Result thislevel = new Result();
thislevel.Datetime = DateTime.Now;
thislevel.result = GlobalVariable.CorrectDecimal;
thislevel.TimesTable = GlobalVariable.NeedsHelpWith;
//submit them
try
{
client.SubmitResultAsync(thislevel);
}
catch
{
MessageBox.Show("Error uploading data");
}
finally
{
client.Close();
Results r3 = new Results();
this.NavigationService.Navigate(r3);
}
}
WCF Test Client:
Cheers,
Nick
If I may, here's a pattern for managing our asynchronous calls between our WPF applications and our WCF Services.
In this section we have a public accessor to our service client that ensures that the connection to the client is open prior to calling a service method:
public static MyServiceClient Client
{
get
{
return GetMyServiceClient();
}
}
private static MyServiceClient client;
private static MyService.MyServiceClient GetMyServiceClient()
{
VerifyClientConnection();
return client;
}
private static void VerifyClientConnection()
{
if (client == null || client.State == System.ServiceModel.CommunicationState.Closed)
{
client = new MyService.MyServiceClient();
}
}
And in this section is an example of our asynchronous call and callback pattern (this example shows the delegate and callback we're using for passing exception data to our service):
public delegate void LogExceptionCompletedEvent();
public static LogExceptionCompletedEvent LogExceptionCompleted;
public static void LogExceptionAsync(SilverlightException exception)
{
string json = JsonConvert.SerializeObject(exception);
Client.LogExceptionCompleted -= client_LogExceptionCompleted;
Client.LogExceptionCompleted += client_LogExceptionCompleted;
Client.LogExceptionAsync(json);
}
private static void client_LogExceptionCompleted(object sender, AsyncCompletedEventArgs e)
{
if (LogExceptionCompleted != null)
{
LogExceptionCompleted();
}
}
In this example, a view model could attach an event handler to the LogExceptionCompleted delegate and in turn receive the result of the callback when it returns from the service.
We basically repeat this pattern for the asynchronous WCF service calls we need to make from our application and it keeps them very organized as well as unit testable.
I'm using the "Post" method so I can send a custom object. But I keep getting the following exception on the request.BeginGetResponse():
{System.Net.ProtocolViolationException: Operation is not valid due to the current state of the object.
at System.Net.Browser.BrowserHttpWebRequest.BeginGetResponseImplementation()}
public void Send()
{
HttpWebRequest client = WebRequest.Create(new Uri(BaseUrl)) as HttpWebRequest;
client.Method = "POST";
client.ContentLength = MaxSerializationSize;
client.BeginGetRequestStream(new AsyncCallback(RequestProceed), client);
}
private void RequestProceed(IAsyncResult asuncResult)
{
HttpWebRequest request = (HttpWebRequest) asuncResult.AsyncState;
StreamWriter postDataWriter = new StreamWriter(request.EndGetRequestStream(asuncResult));
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(MyCustomClass));
ser.WriteObject(ms, MyCustomClassObject);
postDataWriter.Write(ms);
postDataWriter.Close();
request.BeginGetResponse(new AsyncCallback(ResponceProceed), request);
}
private void ResponceProceed(IAsyncResult asuncResult)
{
var request = (HttpWebRequest) asuncResult.AsyncState;
using (var resp = (HttpWebResponse) request.EndGetResponse(asuncResult))
{
using (var stream = resp.GetResponseStream())
{
}
}
}
I have tried so many ways to get this to work. Hoping someone can tell me where i'm going wrong. Thanks.
You need to close the request stream. You are just closing the StreamWriter and not the underlying request stream. While you are at it eliminate the superflous MemoryStream and have the DataContractJsonSerializer write directly to the Request stream.
HttpWebRequest request = (HttpWebRequest) asuncResult.AsyncState;
using (Stream outStream = request.EndGetRequestStream(asyncResult));
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(MyCustomClass));
ser.WriteObject(outStream, MyCustomClassObject);
outStream.Flush();
outStream.Close();
}
request.BeginGetResponse(new AsyncCallback(ResponceProceed), request);
Also get rid of this line:-
client.ContentLength = MaxSerializationSize;
Without also turning off AllowWriteStreamBuffering (which is only possible on the ClientHTTP implementation anyway) there is no need to set the ContentLength, that is done for you.
This code is being used to validate if an email exists in the database. The service return the values fine because it was tested with WCF Storm. In the code I am trying to call this method which return an object (validationResponse). If validationResonse has a true key I want to throw the ValidationException. What i think is happening is SL is making the call asyn and then moving one to he next line of code. How can I call a WCF method and get its reponse and act on it?
public string email
{
get
{
return _email;
}
set
{
vc.emailAddressCompleted += new EventHandler<emailAddressCompletedEventArgs>(vc_emailAddressCompleted);
vc.emailAddressAsync(value);
//Fails here with a null reference to vr (vr is declared futher up)
if (vr.isValid == false)
{
throw new ValidationException(vr.validationErrors);
}
this._email = value;
}
}
void vc_emailAddressCompleted(object sender, emailAddressCompletedEventArgs e)
{
//this never gets executed
this.vr = e.Result;
}
In silverlight all service calls are made asynchronously, in other words you can't call the service synchronously and wait for the reply. So what is happening in your code is vr is null and the exception is being thrown before the service call returns. You could change your code to something like this:
vc.emailAddressCompleted +=
new EventHandler<emailAddressCompletedEventArgs>(vc_emailAddressCompleted);
vc.emailAddressAsync(value);
//this while loop is not necessary unless you really want to wait
//until the service returns
while(vr==null)
{
//wait here or do something else until you get a return
Thread.Sleep(300);
}
//if you got here it means the service returned and no exception was thrown
void vc_emailAddressCompleted(object sender, emailAddressCompletedEventArgs e)
{
//should do some validation here
if (e.Error!=null) throw new Exception(e.Error.ToString());
vr = e.Result;
if (!vr.isValid)
{
throw new ValidationException(vr.validationErrors);
}
_email = value;
}
Is there any existing plumbing to run WCF calls in batches in a BackgroundWorker?
Obviously since all Silverlight WCF calls are async - if I run them all in a backgroundworker they will all return instantly.
I just don't want to implement a nasty hack if theres a nice way to run service calls and collect the results.
Doesnt matter what order they are done in
All operations are independent
I'd like to have no more than 5 items running at once
Edit: i've also noticed (when using Fiddler) that no more than about 7 calls are able to be sent at any one time. Even when running out-of-browser this limit applies. Is this due to my default browser settings - or configurable also. obviously its a poor man's solution (and not suitable for what i want) but something I'll probably need to take account of to make sure the rest of my app remains responsive if i'm running this as a background task and don't want it using up all my connections.
I think your best bet would be to have your main thread put service request items into a Queue that is shared with a BackgroundWorker thread. The BackgroundWorker can then read from the Queue, and when it detects a new item, initiate the async WCF service request, and setup to handle the AsyncCompletion event. Don't forget to lock the Queue before you call Enqueue() or Dequeue() from different threads.
Here is some code that suggests the beginning of a solution:
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace MyApplication
{
public class RequestItem
{
public string RequestItemData { get; set; }
}
public class ServiceHelper
{
private BackgroundWorker _Worker = new BackgroundWorker();
private Queue<RequestItem> _Queue = new Queue<RequestItem>();
private List<RequestItem> _ActiveRequests = new List<RequestItem>();
private const int _MaxRequests = 3;
public ServiceHelper()
{
_Worker.DoWork += DoWork;
_Worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
while (!_Worker.CancellationPending)
{
// TBD: Add a N millisecond timer here
// so we are not constantly checking the Queue
// Don't bother checking the queue
// if we already have MaxRequests in process
int _NumRequests = 0;
lock (_ActiveRequests)
{
_NumRequests = _ActiveRequests.Count;
}
if (_NumRequests >= _MaxRequests)
continue;
// Check the queue for new request items
RequestItem item = null;
lock (_Queue)
{
RequestItem item = _Queue.Dequeue();
}
if (item == null)
continue;
// We found a new request item!
lock (_ActiveRequests)
{
_ActiveRequests.Add(item);
}
// TBD: Initiate an async service request,
// something like the following:
try
{
MyServiceRequestClient proxy = new MyServiceRequestClient();
proxy.RequestCompleted += OnRequestCompleted;
proxy.RequestAsync(item);
}
catch (Exception ex)
{
}
}
}
private void OnRequestCompleted(object sender, RequestCompletedEventArgs e)
{
try
{
if (e.Error != null || e.Cancelled)
return;
RequestItem item = e.Result;
lock (_ActiveRequests)
{
_ActiveRequests.Remove(item);
}
}
catch (Exception ex)
{
}
}
public void AddRequest(RequestItem item)
{
lock (_Queue)
{
_Queue.Enqueue(item);
}
}
}
}
Let me know if I can offer more help.