Reputation: 1200
I'm working in .NET 4 and SL 4. I'm wanting to abstract out the data retrieval portion of my DAL from my silverlight page's code behind. Ideally into its own DLL as an interface layer between my silverlight application and REST API calls. My intent is not to use RIA services as I already have an existing DAL that's making use of a DLL that makes REST API calls.
The issue is the asynchronous "WebClient" call back process. I know that I can utilize the WebClient class to make a REST call and then register the asynchronous handler to simply bind the results from that call to the UI. But in my case, I want to abstract this out into its own DLL. Basically....making it synchronous.
By definition an asynchronous call has issues here being that it can't immediately and directly return an IEnumerable of some type.
Can anyone point me to a sample/tutorial where something like this is being done?
RIA makes method call to separate DLL for IEnumerable data collection
That method makes REST API call to retrieve data, then returns that IEnumerable to the RIA and is bound to the UI.
Upvotes: 1
Views: 776
Reputation: 189457
Boling it right down to the fundementals what you are actually asking for is to be able to make a synchronous call into an API that performs an asynchronous task but only returns when that asynchronous task is complete. To state it another way you want to re-combine the Begin and End phases of an asynchronous operation back into a single atomic synchronous operation.
The only way to acheive that is to block the thread making the call until the end phase of the asynchronous operation is reached. For a number of reasons this is not a good idea.
If you are serious about using Silverlight you have to swallow its asynchronous nature and work with in that framework rather than attempting to force it back into a synchronous system.
Converting synchronous to asynchronous
Have a read of this blog about converting synchronous code into asynchronous code. Having read that now lets just imagine that your code uses a synchronous method in your new DLL called DownloadYourDataObjects
which has this imaginary signature:-
public IEnumerable<YourDataObject> DownloadYourDataObjects(Uri source);
Internally it use WebClient
to download a string from a REST base service and converts it to a set of YourDataObject
instances. The imaginary synchronous code to display this set of data might be:-
private void btnLoadMyData_Click(object sender, RoutedEventArgs e)
{
try
{
LoadMyData();
}
catch (Exception err)
{
// Oops something bad happened show err.
}
}
private void LoadMyData()
{
DataItemsListBox.ItemsSource = DownloadYourDataObjects(someUri);
}
Since Silverlight WebClient
is asynchronous we need to convert this whole chain of code to work in an asynchronous manner.
Using the AsyncOperationService
from the blog we first need to convert the DownloadYourDataObjects
to return an AsyncOperation
instead. It would have a signature like this (see later for an implementation idea):-
public AsyncOperation DownloadYourDataObjects(Uri source, Action<IEnumerable<YourDataObject>> returnResult);
The usage code would then look something like this:-
private void btnLoadMyData_Click(object sender, RoutedEventArgs e)
{
LoadMyData().Run(err =>
{
if (err != null)
{
// Oops something bad happened show err.
}
});
}
private IEnumerable<AsyncOperation> LoadMyData()
{
yield return DownloadYourDataObjects(someUri, result =>
{
DataItemsListBox.ItemsSource = result;
});
}
This may look a litte OTT but in fact it isn't much more code than the original "synchronous" version. In this simple case LoadMyData
only had one operation to perform. A more complex version of LoadMyData
may have multiple other operations that need to be asynchronous as well. In such a case those operations would just be other yield
points in the code, the basic logical structure of LoadMyData
would not change much from an original synchronous version.
Here is an example of an implementation of DownloadYourDataObjects
that your DLL would supply.
public AsyncOperation DownloadYourDataObjects(Uri source, Action<IEnumerable<YourDataObject>> returnResult)
{
return (completed) =>
{
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, args) =>
{
try
{
returnResult(ConvertStringToYourDataObjects(args.Result));
completed(null);
}
catch (Exception err)
{
completed(err);
}
};
client.DownloadStringAsync(source);
};
}
Upvotes: 2