Archive for January 2009
In the article Async Async – Using BackgroundWorker and WebClient async upload together (Part 1) I covered the use of the BackgroundWorker and planed to use the WebClient to upload files via FTP.
And so it was that WebClient didn’t support timeouts, nor did it error out correctly if the receiving FTP sever didn’t correctly.
When I moved testing from my local test machine to a staging server smaller FTP uploads worked like a charm, but larger files (50 MB) plus sever seemed to complete. So I stopped using System.Net.WebClient for FTP uploads and switched to FTPWebRequest so I could investigate where the hang up was. Here is the code for using FTPWebRequest to upload a file:
Here is what it does:
1) Not shown yet – but this method is called inside a try/finally block so it should not bring the process down.
2) A FileStream is opened based on a object level variable. FileStream allows us to copy out portions of the file instead of the whole thing.
3) A RequestStream is opened from FTPWebRequest. This will allow is to pour in data from file stream and send it to the server.
4) A simple Do/While loop is used to transfer file bytes.
5) When the write process is complete is track in a local variable that the write process is completed.
6) We clean up.
So good so far. If the code looks familiar that’s it because it’s from the FtpWebRequest documentation.
But let’s take a look inside two more code blocks. Here is the method that calls OnExcecuteProcess:
Nothing really dramatic. We build the request, set the method, credentials and timeout and pass to OnExecuteProcess. The ‘catch’ block contains a ‘request.Abort’ call and logs the exception. The ‘finally’ block the IsComplete property and calls OnUploadComplete, which triggers a corresponding event.
But here is where we compensate for the failed request completion or response in the Error property.
In the first code block I set isWriteComplete to true after writing my bytes. Here is where I am using the isWriteComplete property to control error returns.If the logging code sees an error it logs a non-download, so this code avoids returning an error if all the bytes were written to the response stream.
Now as I am writing here I am thinking it’s probably better to return an IsWriteComplete property along with IsComplete, always return the exception, and pass along the logic to the logging tool. I’ll try that and get back with you.
I was doing layout for a revision of Christwebs.com today and I was following a fairly well established pattern of using CSS to absolutely position a footer at the bottom of the page – always and regardless of page length. In the process of trying to get the CSS to work I stumbled on a great, concise tutorial on how to do this. Followed it all, again, and I got it working in Firefox and Safari but sure enough I had trouble with IE 7.
Well, I think I found a solution. The IE7.js script does a good job of fixing up IE 6 difficulties and making it work similar to IE 7. But one thing I also remembered is that there is an IE8.js script (here is the link to both on Google Code) that can fix up issues with IE 7. Sure enough, attaching the the IE8.js script fixed it up nicely. No complaints here.
Yep. Some people think it maybe all an illusion. It would be interesting if so.
Looks like we’ll have to wait a bit longer for the delayed Connector/net 6.0 with EF support. Looks like Sun is having ‘issues’ with the SQL generator and a rewrite for that part of the engine is underway.
The discussion on using BackgroundWorker here is good but I had to abandon the use of the WebClient class inside the upload object.
Doing async with the BackgroundWorker class (System.ComponentModel) is easy, but when I realized that async could also be done with the WebClient class (System.net) while the BackgroundWorker is executing I had to give it a try so that I could support cancelation. It works, but you have to follow the BackgroundWorker async pattern carefully.
In my case I created a Delivery class that has the responsibility of setting up the BackgroundWorker, staging ftp execution, and clean up. I have an Upload object that manages the async FTP upload separately . This Upload object is created when Delivery.Execute is called and then passed along to the BackgroundWorker.DoWork event hander, named InitDelivery, for execution. Here’s the code for InitDelivery:
Here is how it works:
1) The Upload object is retrieved from the e.Argument.
2) The Upload object is passed to a local field so that it’s internal async operation can be canceled if need be.
3) The method Upload.ExecuteAsync is called.
4) Next we wait for the ‘while(Upload.IsBusy)’ to compete. But you might ask why I am doing this if I am using an async in the Upload object? Here are the reasons:
- The BackgroundWorker is designed for managing long running, synchronous tasks so we need to mimic that pattern inside the BackgroundWorker.DoWork event handler.
- The Upload.IsBusy state is always set to false when the internal async operation finishes regardless of completion, cancelation, or exception.
- I need to use state from the Upload object in the BackgroundWorker.RunWorkerCompleted event handler.
5) The Upload object is passed to e.Result and the event handler exits.
The BackgroundWorker.RunWorkerCompleted event is wired to DeliveryFinish. Here is the code:
Here is how it works:
1) The Delivery.Error property is set from e.Error and the Upload object is retrieved from e.Result.
2) If the Upload object has an error then it is forwarded to the Delivery.Error property.
3) Perform any logging and clean up as necessary.
4) The Try/Finally block ensures that Delivery.IsFinished is always set to true.
Looks like the ‘Upload.IsBusy’ thread really hammers the processor. To remove that I’ve added a Thread.Sleep call that really dampens things down, and it does keep the transfer working.
That’s it for Part 1. In Part 2 I’ll look at the Upload object and cover a few highlights. Here is the link: