Mike-Ward.Net

Async Event Handlers in C#

Recently, C# added a nifty async/await facility. I have not used it extensively due to the new and unfamiliar async/await declarations. I’m slowly warming up to it however. Here’s a two-liner that speaks volumes to the expressiveness of the aysnc/await.

hyperlink.ToolTip = link;  
hyperlink.ToolTipOpening += async (s, e) => hyperlink.ToolTip = await LongUrl.Lookup(link);

This works beautifully. The tooltip on the hyperlink opens immediately with the shortened URL (link). The handler then tries to find the original URL that the shortened URL resolves to using an online service (once the long URL is found, it’s cached). When the long URL is resolved the tooltip instantly updates.

It can take several seconds to resolve this so a blocking operation is not appropriate. With the async/await keywords, it reads like a synchronous operation, but works asynchronously.

The LongUrl.Lookup method needs to be marked async as well. Here’s the implementation for reference.

public static class LongUrl  
{  
    private static readonly ConcurrentDictionary<string, string> ShortToLongUrl =   
        new ConcurrentDictionary<string, string>();  

    public async static Task<string> Lookup(string link)  
    {  
        try  
        {  
            string longUrl;  
            if (ShortToLongUrl.TryGetValue(link, out longUrl))  
            {  
                return longUrl;  
            }  

            var url = "http://api.longurl.org/v2/expand?format=json&url=" + OAuth.UrlEncode(link);  
            var request = WebRequestWrapper.Create(new Uri(url));  
            request.UserAgent = "tweetz/5.0";  
            request.Timeout = 1500;  
            using (var response = await request.GetResponseAsync())  
            {  
                var serializer = new DataContractJsonSerializer(typeof (LongUrlResponse));  
                var longUrlResponse =   
                    (LongUrlResponse)serializer.ReadObject(response.GetResponseStream());  
                if (string.IsNullOrWhiteSpace(longUrlResponse.LongUrl) == false)  
                {  
                    if (ShortToLongUrl.Count > 1000) ShortToLongUrl.Clear();  
                    ShortToLongUrl.TryAdd(link, longUrlResponse.LongUrl);  
                    return longUrlResponse.LongUrl;  
                }  
            }  
        }  
        catch (Exception ex)  
        {  
            Console.WriteLine(ex.Message);  
        }  
        return link;  
    }  
}  

[DataContract]  
public class LongUrlResponse  
{  
    [DataMember(Name = "long-url")]  
    public string LongUrl { get; set; }  
}

At compile time, the async/await mechanism injects a significant chunk of code in the form of a state machine to handle waiting for a response. In many cases this a fair trade-off given the economy of expression and “serial-like” layout of code.

← newer older →
.Net, Technology, Life, Whatever

Recent Posts

Quick Tip - Clear ReSharper's Cache to Fix False Errors
Quick Tip - Refined GitHub
Vertical Layout for HTML Tables
Visual Studio 2017 Installer Quick Tip
VSColorOutput for VS 2017 Released
Quick ReSharper tip for inline templates
NoSQL vs. NewSQL: What's the Difference?
More... (1077)

Donate with PayPal