3

I am working on some software that is making calculations on the server side. I have tried using BackgroundWorker and it was working perfectly fine until I tried switching from fat client to a web service. The error (which I was already expecting due to what my colleagues told me) "BackgroundWorker is not marked as Serializable" occured. The goal of my application is to update the client with the progress of the calculation as well as the name of the record currently being calculated. If needed I can post some code, but in this case I doubt it is really relevant to the problem. The scheme of what I am trying to achieve could be shown like this:

  • Client hits button "prepare"
  • Server gets the call
  • Server sends the data
  • Client receives the records list to confirm
  • Client confirms their choice (I then set up the background worker and send it to the server)
  • Server gets the call (receives the background worker's reference, as well as all the records needed to calculate at once)
  • With a loop I run through the records (each calculation takes ~1-3 seconds), update BackgroundWorker's status like this:

    worker.ReportProgress(i * 100 / ids.Length, "Currently counting: " + id + "...");
    Calculate(ids[i]);
    worker.ReportProgress((i+1) * 100 / ids.Length, "Currently counting: " + id + "...");
    

Is there any way to achieve what I am trying to to work also on the web service too? I assume it would require either tricking BackgroundWorker into Serializable or maybe there are other ways to update the status from the server to the client?

P.S. I am not sending very much data back from the server (I could actually even try to send even less data like id only), so I assume that won't be a problem of the overload.

Andrius Naruševičius
  • 8,348
  • 7
  • 49
  • 78

3 Answers3

4

Basically when you're writing code for the web, you're dealing with an entirely different model, based on request/response rather than continual interaction.

You should probably make your initial request return a sort of "task ID" (which can be any unique ID), and return that quickly. Meanwhile, you start the task in the background. The client can then poll for the status of that particular task. You could use "long polling" where each request hangs around until there's a change in status, or just make the client call the server regularly. Either way, the response will just be the progress (the number of records processed and the name of the current record).

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thank you for your answer. I went with *just make the client call the server regularly*, but I would like to ask how would you do *each request hangs around until there's a change in status*. I successfully made this with a loop which continously checks the value change and only then allows the function to return the new value. However it loops millions of times doing nothing which looked pretty scary :( In short my question would be: how to do this `while(!green_light){/*wait at the stop line = do nothing*/} drive();` without looping million of times and in the easiest way possible? – Andrius Naruševičius Oct 18 '12 at 10:47
  • Yes, I could do some Thread.Sleep() there, but I don't want to waste precious time, if, for example, the value changes after 1.05s and I do Thread.Sleep(1000) thus checking at 0s, 1s, 2s and thus wasting 0.95s? – Andrius Naruševičius Oct 18 '12 at 10:51
  • @AndriusNaruševičius: Implementing long-polling is going to be trickier - significantly trickier. There are various frameworks to help at least for AJAX/browser interaction (e.g. SignalR) - I don't know how well they work for web services. You may well be better looking at asynchronous web service support (especially with .NET 4.5). I would definitely start off with the simpler approach though. You definitely want a sleep rather than a tight loop - is it *really* a problem if a user has to wait an extra second to see progress? – Jon Skeet Oct 18 '12 at 10:59
  • Yes, I guess there is not much of a problem, just wanted to seek perfection, hehe :) Thanks again :) – Andrius Naruševičius Oct 18 '12 at 11:08
0

I am afraid that you are trying to serialize instance of a class which is having a background worker member variable. It is not possible to do so as background worker is not a serializable object.

What you can do here is -

  • Create a class which is having a method which runs asynchronously.
  • Create a member variable in that class which stores the progress of background worker
  • Create a web method in class which returns the value of progress.

Now on your page you need to

  • Invoke the method that has to run asynchronously.
  • put a client side javascript which executes on regular interval. It should query the web service and ask the progress.
    • Update the page and show the progress.

I hope I have explained it properly.

Murtuza Kabul
  • 6,438
  • 6
  • 27
  • 34
0

If you wish to get update on process from time to time (on completion of some milestone) over a service, what I shall suggest you to use async service. Use WCF and create a service that is async (You ma look at topics like this: http://blogs.msdn.com/b/rjacobs/archive/2011/06/30/how-to-implement-a-wcf-asynchronous-service-operation-with-task-lt-t-gt.aspx).

Create some method like

public int onBlockComplete(IAsyncResult result)
{
    //...
}

And handle this in your client. Also implement a method to callback when the entire process is completed.

In this way you may handle the block complete event and show the progress on the client.

Of course, if you implement the client in ASP.NET, you shall have to refresh the page to see the update.

Kangkan
  • 15,267
  • 10
  • 70
  • 113