Can you tell whether the following snippets are correctly binding to an asynchronous data source ?
While it seems to be working, i.e : UI does not freeze, I'm not entirely sure about the correctness as the MSDN documentation does not really talk about binding to 'async' methods in these docs :
Binding.IsAsync
ObjectDataProvider.IsAsynchronous
<pages:HottestPageProxy x:Key="PageProxy" ></pages:HottestPageProxy>
<ObjectDataProvider x:Key="DataProviderArtists" IsAsynchronous="True" ObjectInstance="{StaticResource PageProxy}" MethodName="GetArtists">
<ObjectDataProvider.MethodParameters>
<system:String>Grabbing artists !</system:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
(HottestPageProxy object is a small helper that provides data for the controls)
public class HottestPageProxy
{
[UsedImplicitly]
public async Task<ArtistsQuery> GetArtists([CallerMemberName] string memberName = "")
{
Console.WriteLine(memberName);
string apiKey = App.GetApiKey();
Task<ArtistsQuery> topHottt = Queries.ArtistTopHottt(new ArtistTopHotttParameters
{
ApiKey = apiKey,
Results = 100,
Buckets = new[] {ArtistTopHotttBuckets.Hotttnesss}
});
return (await topHottt);
}
}
EDIT : method that 'await topHottt' calls
public static async Task<ArtistsQuery> ArtistTopHottt(ArtistTopHotttParameters parameters)
{
if (parameters == null) throw new ArgumentNullException("parameters");
return await Get<ArtistsQuery>(parameters);
}
private static async Task<T> Get<T>(Parameters parameters) where T : Query
{
if (parameters == null) throw new ArgumentNullException("parameters");
ValidateParameters(parameters);
string url = parameters.GetQueryUrl();
var value = new Uri(url);
using (var client = GetHttpClient())
using (var message = await client.GetAsync(url))
{
// fetch message content (removed)
return GetQueryResultObject<T>(s);
}
}
private static T GetQueryResultObject<T>(string json) where T : class
{
// create T from Json string (removed)
return t;
}
EDIT using AsyncEx
Using your library works though the syntax now is :
<ItemsControl x:Name="ItemsControlTopHott"
ItemsSource="{Binding ... Path=Artists.Result.Artists ...}">
</ItemsControl>
Is 'Artists.Result.Artists' really what you expect me to use ? :)
The new syntax makes it more confusing as the source is :
public sealed class ArtistsQuery : Query
{
public List<Artist> Artists { get; set; }
}
Not a big deal but if I could avoid such syntax that'd be great.
You said that .Result might bring a deadlock, did I miss something in implementing your solution then ?
Artists.PropertyChanged event raised the following messages :
I'll give a try to .ConfigureAwait(false) as you've mentioned in your post to see how it goes with it.
Forgot to mention, actually my implementation using .Result indeed does not block the UI as the typical time to get the result is a few seconds; I'd have seen the UI freeze. It seems right ... but I'm not certain whether it's correct, hence my question.
As others have noted, the "asynchronous" members in WPF types have nothing to do with async
and await
.
You do have a problem in your binding; your path is using Task.Result
, which will block the UI thread until the query is complete. Also, using Result
brings up the possibility of a deadlock that I describe on my blog.
I have another blog entry that deals with async
properties, in particular how to data-bind to properties that are (logically) asynchronous. My AsyncEx library has a type called NotifyTaskCompletion
that allows you to data-bind more naturally to an asynchronous Task
.
So, e.g., you could do something like this:
public class HottestPageProxy
{
public HottestPageProxy()
{
Artists = NotifyTaskCompletion.Create(GetArtists());
}
public INotifyTaskCompletion<ArtistsQuery> Artists { get; private set; }
private Task<ArtistsQuery> GetArtists()
{
string apiKey = App.GetApiKey();
return Queries.ArtistTopHottt(new ArtistTopHotttParameters
{
ApiKey = apiKey,
Results = 100,
Buckets = new[] {ArtistTopHotttBuckets.Hotttnesss}
});
}
}
Then you can data-bind to several different properties on INotifyTaskCompletion<T>
, including IsCompleted
, ErrorMessage
, and Result
(which does not block; it will return default(T)
if the task is not completed).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With