ASMX webservices with Silverlight Async confusion - c#

I have a silverlight 4 web app that needs to communicate with a server by accessing the ASMX web service on the server.
I have a list(yes, the array), of objects that I need to send(one by one) as a parameter to the service. However looping through the list and running the method(objecttosend); will not work because I need to send then one after another and Silverlight seems to only support Async(presumably to not lockup interface - makes sense).
So I tried this:
public void SendNextPart()
{
if (partsToSend.Count > 0)
{
Part thisPart = partsToSend.Dequeue();
fuWS.createPartCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(fuWS_createPartCompleted);
fuWS.createPartAsync(thisPart);
}
}
Queue<Part> partsToSend = new Queue<Part>();
void fuWS_createPartCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
SendNextPart();
}
Which, as far as I can understand it, will check to see if the List has parts to send, then run the webservice(called fuWS) method and delete that part from the partsToSend List. Once it gets the completed event it should then run the SendNextPart method again and send the next part.
However what is happening(picked this up by watching HTTPwatch) is that it sends the first part, then after that is sends 2 parts at once and then after that more and more, all at once. Almost as if it is receiving the completed event before it has actually sent to the server and run the method successfully.
Please help, this is bugging the hell out of me, and it completely breaks what I need to do :'(

I don't see the SendNextBuffer method that you're calling in the web service callback event handler. But in any case, at best your code has a race condition. If the web service completes and returns before the partsToSend.RemoveAt line is executed (theoretically possible) then you could be making the next request before you've removed the one you just sent.
So first, you should check to make sure you've included all the code in your example unless you meant for SendNextBuffer to say SendNextPart.
Secondly, you should move the partsToSend.RemoveAt line before the web service call.
Finally, you should probably change the partsToSend list into a Queue<Part> (first in, first out) or Stack<Part> (last in, first out) instead since that is what you're using it as.

Ok, so after using Debug.WriteLine, I realized that I was being an idiot.
Check out this line:
fuWS.createPartCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(fuWS_createPartCompleted);
What this was doing was adding a new event handler every time it had to send a new part. So the second part sending now had two callback then the third would have more and so on increasing exponentially.

Related

How Implementing a windows service on a server that is hosted with ISP

I am working on an assignment in asp.net to send notification email to users at specific intervals.
But the problem is that since the server is not privately owned i cannot implement a windows service on it.
Any ideas?
There's no reliable way to achieve that. If you cannot install a Windows Service on the host you could write a endpoint (.aspx or .ashx) that will send the email and then purchase on some other site a service which will ping this endpoint at regular intervals by sending it HTTP request. Obviously you should configure this endpoint to be accessible only from the IP address of the provider you purchase the service from, otherwise anyone could send an HTTP request to the endpoint and trigger the process which is probably undesirable.
Further reading: The Dangers of Implementing Recurring Background Tasks In ASP.NET.
There are several ways to get code executing on an interval that don't require a windows service.
One option is to use the Cache class - use one of the Insert overloads that takes a CacheItemRemovedCallback - this will be called when the cache item is removed. You can re-add the cache item with this callback again and again...
Though, the first thing you need to do is contact the hosting company and find out if they already have some sort of solution for you.
You could set up a scheduled task on the server to invoke a program with the desired action.
You can always use a System.Timer and create a call at specific intervals. What you need to be careful is that this must be run one time, eg on application start, but if you have more than one pools, then it may run more times, and you also need to access some database to read the data of your actions.
using System.Timers;
var oTimer = new Timer();
oTimer.Interval = 30000; // 30 second
oTimer.Elapsed += new ElapsedEventHandler(MyThreadFun);
oTimer.Start();
private static void MyThreadFun(object sender, ElapsedEventArgs e)
{
// inside here you read your query from the database
// get the next email that must be send,
// you send them, and mark them as send, log the errors and done.
}
why I select system timer:
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
more words
I use this in a more complex class and its work fine. What are the points that I have also made.
Signaling the application stop, to wait for the timer to end.
Use mutex and database for synchronize the works.
Easiest solution is to exploit global.asax application events
On application startup event, create a thread (or task) into a static singleton variable in the global class.
The thread/task/workitem will have an endless loop while(true) {...} with your "service like" code inside.
You'll also want to put a Thread.Sleep(60000) in the loop so it doesn't eat unnecessary CPU cycles.
static void FakeService(object obj) {
while(true) {
try {
// - get a list of users to send emails to
// - check the current time and compare it to the interval to send a new email
// - send emails
// - update the last_email_sent time for the users
} catch (Exception ex) {
// - log any exceptions
// - choose to keep the loop (fake service) running or end it (return)
}
Thread.Sleep(60000); //run the code in this loop every ~60 seconds
}
}
EDIT Because your task is more or less a simple timer job any of the ACID type concerns from an app pool reset or other error don't really apply, because it can just start up again and keep trucking along with any data corruption. But you could also use the thread to simply execute a request to an aspx or ashx that would hold your logic.
new WebClient().DownloadString("http://localhost/EmailJob.aspx");

What's the correct way of posting this data asynchronously, and canceling/queuing new requests?

I am attempting to improve the stability of the web dashboard I have created. I have noticed that the main cause of getting into a bad state is when the user moves too quickly and requests get cut short.
For instance, I have the following javascript:
//When a resize occurs a lot of handling needs to happen to convert the user's action into
//a visibly-pleasing result. In addition, the server has to be spoken with in order to save the controls.
function OnClientResizing(pane, eventArgs) {
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
This function passes the hard work back to the server, so that it can calculate the appropriate ways to resize the controls on the page during resizes. This takes X seconds. If the user then resizes the page again before X seconds has elapsed -- I enter into a bad state. Either the old request gets cut off prematurely, or the new one runs at the same time. Either way, controls become mishapen on the page.
As such, I would like to queue future resizes, or play around with canceling current requests. I read that the best way to do something like this is to simply set a flag outside the scope of this function. I can do that, but I am not sure how to detect the end of a doPostBack. Am I supposed to change the javascript variable from the server-side somehow in PageRequestManager - EndRequest?
Cheers
First off, don't let your server participate in UI resize algorithms. Do that entirely client side. You can send resulting data to the server at any time, but don't make a real-time UI positioning depend upon a server response. That should be handled client-side with CSS or javascript logic.
Second off, if your code can't handle two ajax calls in flight at the same time, then your options are as follows:
Fix your code so it can handle sequential ajax responses in flight at the same time.
Cancel/ignore the first ajax response the moment you send a second one so that you ignore the response from the first and wait for the response from the second.
Prevent a second ajax request until the first one completes. I wouldn't suggest queueing them because that's just going to lead to an even worse user experience.
The details of how to do 1, 2 or 3 depend upon how your code works which you have not yet shared.
The easiest is option 3). That can be done with just a global flag. Just define a global variable, set it to true when you start an ajax call and clear it when the ajax call completes (in a completion function):
var ajaxInFlight = false; // global declaration
function OnClientResizing(pane, eventArgs) {
if (ajaxInFlight) return; // do nothing if ajax call already in flight
ajaxInFlight = true;
eventArgs.set_cancel(true);
var parameters = new Array();
parameters.push("Resize");
parameters.push(pane.get_id());
parameters.push(eventArgs.get_delta());
__doPostBack(pane.get_splitter()._uniqueID, parameters);
}
function postBackCompletionHandler(id, parms) {
ajaxInFlight = false; // clear global flag, ajax call done
// ... rest of your function here
}
You will also have to make sure that error conditions are handled so that the global flag is reset if the ajax call fails for any reason.

silverlight communication between client and server

Sorry I cannot post the code but here is what is happening in detail.
User opens a form
it adds an event listener like:
GP.GlobalInfo.CommWCF.serviceClient.GetFilteredMessageCompleted += new EventHandler<GetFilteredMessageCompletedEventArgs>(serviceClient_GetFilteredMessageCompleted);
and then sends a asynchronous message to the server to get required data from database.
at the same time, the form adds another event listener and then sends another message:
GP.GlobalInfo.CommWCF.serviceClient.GetFilteredMessageCompleted += new EventHandler<GetFilteredMessageCompletedEventArgs>(serviceClient_GetFilteredMessageCompletedAnother);
so the client (form) is waiting for two messages to be returned.
as soon as messages are returned the form will display the data in a grid.
It seems not working as I expect. somehow messages get stuck somewhere.
so, I would like to know if my approach is wrong.
Another case is, there are five objects on the screen that I can click.
These five objects will use the same Class to create.
clicking an object brings a form with corresponding data.
if I click the first object, the form will add an event listener and sends a message to the server.
while the form is waiting for the data, I close the form, and then open another form by clicking another object.
The new form will add an event listener and sends a new message to the server.
Meantime, the server has been working the first form message, and returns the result to the second form.. not the first one(this has been closed).
this is what I experience right now.
can anybody give me some solution ASAP for the better behavior and understanding of silverlight and WCF.
I think I should send a stop message to the server or something..
-------old question----------
Hi I seem to have some problem with communicating between client and server .
when a form is opened, there are three listeners are opened. Each listener gets the required data.
I think (not sure) silverlight gets stuck while it communicates using those three listeners at the same time.
So, should I use only one listener running at the same time always in silverlight?
so like,
client asking A data from server
client asking B data from server
client save C data to server
those stuffs cannot be done at the same time ?
Do I always make sure of single communication process between client and server?
Thanks
The problem is that the event handler just tells "when that kind of operation is completed, call me". The event handler is not related to the specific call, but to the kind of operation.
If you are calling the same operation twice you cannot attach two event handlers and expect that each call invokes the respective handler. What happens is that when an operation is completed every handler attached to that operation will be invoked.
You can solve your problem as follows:
attach a single event handler for each specific operation
when you call yourOperation_Async() you can pass an identifier as userState (the last parameter)
inside the event handler you act accordingly to the identifer (you can retrieve it from the yourOperationCompledEventArgs.UserState), for ex. updating the correct form.
dummy example:
...
serviceClient.YourOperationAsync(formName);
...
void serviceClient_YourOperationCompleted(object sender, YourOperationCompletedEventArgs e)
{
if (e.UserState != null && e.UserState is string)
{
string formToUpdate = (string)e.UserState;
...
... update formToUpdate ...
...
}
}

Calling a webservice async

Long post.. sorry
I've been reading up on this and tried back and forth with different solutions for a couple of days now but I can't find the most obvious choice for my predicament.
About my situation; I am presenting to the user a page that will contain a couple of different repeaters showing some info based on the result from a couple of webservice calls. I'd like to have the data brought in with an updatepanel (that would be querying the result table once per every two or three seconds until it found results) so I'd actually like to render the page and then when the data is "ready" it gets shown.
The page asks a controller for the info to render and the controller checks in a result table to see if there's anything to be found. If the specific data is not found it calls a method GetData() in WebServiceName.cs. GetData does not return anything but is supposed to start an async operation that gets the data from the webservice. The controller returns null and UpdatePanel waits for the next query.
When that operation is complete it'll store the data in it's relevant place in the db where the controller will find it the next time the page asks for it.
The solution I have in place now is to fire up another thread. I will host the page on a shared webserver and I don't know if this will cause any problems..
So the current code which resides on page.aspx:
Thread t = new Thread(new ThreadStart(CreateService));
t.Start();
}
void CreateService()
{
ServiceName serviceName = new ServiceName(user, "12345", "MOVING", "Apartment", "5100", "0", "72", "Bill", "rate_total", "1", "103", "serviceHost", "password");
}
At first I thought the solution was to use Begin[Method] and End[Method] but these don't seem to have been generated. I thought this seemed like a good solution so I was a little frustrated when they didn't show up.. is there a chance I might have missed a checkbox or something when adding the web references?
I do not want to use the [Method]Async since this stops the page from rendering until [Method]AsyncCompleted gets called from what I've understood.
The call I'm going to do is not CPU-intensive, I'm just waiting on a webService sitting on a slow server, so what I understood from this article: http://msdn.microsoft.com/en-us/magazine/cc164128.aspx making the threadpool bigger is not a choice as this will actually impair the performance instead (since I can't throw in a mountain of hardware).
What do you think is the best solution for my current situation? I don't really like the current one (only by gut feeling but anyway)
Thanks for reading this awfully long post..
Interesting. Until your question, I wasn't aware that VS changed from using Begin/End to Async/Completed when adding web references. I assumed that they would also include Begin/End, but apparently they did not.
You state "GetData does not return anything but is supposed to start an async operation that gets the data from the webservice," so I'm assuming that GetData actually blocks until the "async operation" completes. Otherwise, you could just call it synchronously.
Anyway, there are easy ways to get this working (asynchronous delegates, etc), but they consume a thread for each async operation, which doesn't scale.
You are correct that Async/Completed will block an asynchronous page. (side note: I believe that they will not block a synchronous page - but I've never tried that - so if you're using a non-async page, then you could try that). The method by which they "block" the asynchronous page is wrapped up in SynchronizationContext; in particular, each asynchronous page has a pending operation count which is incremented by Async and decremented after Completed.
You should be able to fake out this count (note: I haven't tried this either ;) ). Just substitute the default SynchronizationContext, which ignores the count:
var oldSyncContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var serviceName = new ServiceName(..);
// Note: MyMethodCompleted will be invoked in a ThreadPool thread
// but WITHOUT an associated ASP.NET page, so some global state
// might be missing. Be careful with what code goes in there...
serviceName.MethodCompleted += MyMethodCompleted;
serviceName.MethodAsync(..);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSyncContext);
}
I wrote a class that handles the temporary replacement of SynchronizationContext.Current as part of the Nito.Async library. Using that class simplifies the code to:
using (new ScopedSynchronizationContext(new SynchronizationContext()))
{
var serviceName = new ServiceName(..);
// Note: MyMethodCompleted will be invoked in a ThreadPool thread
// but WITHOUT an associated ASP.NET page, so some global state
// might be missing. Be careful with what code goes in there...
serviceName.MethodCompleted += MyMethodCompleted;
serviceName.MethodAsync(..);
}
This solution does not consume a thread that just waits for the operation to complete. It just registers a callback and keeps the connection open until the response arrives.
You can do this:
var action = new Action(CreateService);
action.BeginInvoke(action.EndInvoke, action);
or use ThreadPool.QueueUserWorkItem.
If using a Thread, make sure to set IsBackground=true.
There's a great post about fire and forget threads at http://consultingblogs.emc.com/jonathangeorge/archive/2009/09/10/make-methods-fire-and-forget-with-postsharp.aspx
try using below settings
[WebMethod]
[SoapDocumentMethod(OneWay = true)]
void MyAsyncMethod(parameters)
{
}
in your web service
but be careful if you use impersonation, we had problems on our side.
I'd encourage a different approach - one that doesn't use update panels. Update panels require an entire page to be loaded, and transferred over the wire - you only want the contents for a single control.
Consider doing a slightly more customized & optimized approach, using the MVC platform. Your data flow could look like:
Have the original request to your web page spawn a thread that goes out and warms your data.
Have a "skeleton" page returned to your client
In said page, have a javascript thread that calls your server asking for the data.
Using MVC, have a controller action that returns a partial view, which is limited to just the control you're interested in.
This will reduce your server load (can have a backoff algorithm), reduce the amount of info sent over the wire, and still give a great experience to the client.

Why does my WCF service method only work once I've called MessageBox.Show()?

I have a WCF service which provides a method that creates a file. Sometimes it takes a little while for this file to appear, and other methods which are relying on that file's existence fail if they are called immediately afterward. As a result, I want to check that the file has appeared before proceeding.
In my client class, I can call the service method and then loop until the file has appeared before proceeding - this works perfectly. But if I loop until the file has appeared while still inside the service method, it never finds that the file has been created - unless I call MessageBox.Show() before checking. If I do, it finds it almost immediately, just as if I had called it from the client.
The file definitely exists during the time that the service method is looking for it (Edit: doesn't use File.Exists() as I previously wrote) - so why can't it find it? And why does MessageBox.Show() fix this problem?
I'm assuming it must be a threading issue I don't understand, since it works from outside the service, and works if you call MessageBox.Show() (which blocks the UI thread?) but I'm at a bit of a loss, so any help would be greatly appreciated.
Further info: the service is hosted as a plugin by a running GUI application, if that's relevant to the threading issue. Thanks everyone.
Edit: Here's a version of the code. I didn't post this originally because it uses a third-party library, so I'm not sure how helpful it is:
// The WCF service, in which HasCompiled(name) never
// returns true unless MessageBox.Show() is called:
public void CompileScript(string name)
{
// CompileFile outputs a file to disk:
string debug = NWN2Toolset.NWN2ToolsetMainForm.Compiler.CompileFile(script.Name,GetModuleTempPath());
if (debug.Length > 0)
throw new InvalidDataException("'" + name + "' could not be compiled: " + debug);
// If the following line is commented out, this method never returns:
MessageBox.Show("blabla");
while (!HasCompiled(name));
}
public bool HasCompiled(string name)
{
NWN2GameModule module = GetModule();
OEIResRef cResRef = new OEIResRef(name);
IResourceEntry entry = module.Repository.FindResource(cResRef,resourceType);
return entry != null;
}
// The client class, in which HasCompiled(name) returns true almost immediately:
[Test]
public void TestCompilesScript()
{
service.AddScript(name,scriptContents);
service.CompileScript(name);
while (!service.HasCompiled(name)) {
Console.WriteLine("HasCompiled(" + name+ ") == false.");
}
Console.WriteLine("HasCompiled(" + name+ ") == true.");
}
I don't see your code so I can suggest the following:
either don't return the first method until the file created (you might need to change the default timeouts for WCF)
Or - only after the first call to the Service returns - loop against the service to see if the file exists.
Not seeing your code, I just can guess -
if you are using threads, messagenox.show makes the calls in a certain order (because the system waits for you to dismiss the messagebox).
Instead of looping in the client, you could implement a callback instead. There are lots of good examples out there - http://www.google.com/search?q=wcf+callback+example

Categories