Wednesday, November 24, 2010

Store data from async web service call with Silverlight

Did you ever tried to store data on Silverlight from an async web service call using SaveFileDialog - and get told it doesn’t work?
But that is only half the truth. Here it how it works!
Let’s assume you want to provide the possibility to download files within your Silverlight app. The first attempt to achieve something like that could be consist of the following snippet. (Indeed, naive solution but’s it’s a sample!)
private void Save(string fileName)
{
   FileServiceClient fileServiceClient = new FileServiceClient();
   fileServiceClient.GetFileContentCompleted += (sender, evtArgs) =>
                                                {
                                                   if (!evtArgs.Cancelled && evtArgs.Error == null)
                                                      SaveData(evtArgs.Result);
                                                };
   fileServiceClient.GetFileContentAsync(fileName);
}

private void SaveData(string data)
{
   SaveFileDialog saveFileDialog = new SaveFileDialog
                                   {
                                      DefaultExt = ".txt",
                                      Filter = "Textfile (*.txt)|*.txt|All files|*.*"
                                   };

   if (saveFileDialog.ShowDialog() == true)
   {
      using (Stream stream = saveFileDialog.OpenFile())
      {
         using (StreamWriter streamWriter = new StreamWriter(stream))

            streamWriter.Write(data);

      }
   }
}
This code causes a System.Security.SecurityException due the ShowDialog()-Method is called within the thread which handles the response of the async call and this method only could be called from user-initiated code.
Damn thing right know! Some devs will stop here but let’s take a closer look at the members of this suspicious SaveFileDialog.
There is one method named OpenFile() which opens a Stream to the local file.
You guess it. Bingo! Smiley mit geöffnetem Mund
We start modifying our code to open the stream before starting the call.
private void Save(string fileName)
{
   SaveFileDialog saveFileDialog = new SaveFileDialog
   {
      DefaultExt = ".txt",
      Filter = "Textfile (*.txt)|*.txt|All files|*.*"
   };

   if (saveFileDialog.ShowDialog() == true)
   {
      Stream stream = saveFileDialog.OpenFile();

      FileServiceClient fileServiceClient = new FileServiceClient();
      fileServiceClient.GetFileContentCompleted += (sender, evtArgs) =>
      {
         if (!evtArgs.Cancelled && evtArgs.Error == null)
         {
            using (stream)
            {
               using (StreamWriter streamWriter = new StreamWriter(stream))
                  streamWriter.Write(evtArgs.Result);
            }
         }
      };
      fileServiceClient.GetFileContentAsync(fileName);
   }
}
Now your app is able to save data on the result handler of an async web service call.
The only thing you should keep in mind is proper exception handling to ensure final closing of that stream!

Update:

Code and sample app is available at codinghints.codeplex.com.

No comments:

Post a Comment