3
\$\begingroup\$

I just picked up Async Await and I am trying to use Async Await in Console App along with TPL for querying webservices. The code works fine and I am able to get the speed using HttpClient and async. Wanted to know if I am overusing async/await and if there is anything that I should avoid and can be simplified further.

I have read Albahari's threading e-book and the following articles to understand the usage.

http://www.tugberkugurlu.com/archive/how-and-where-concurrent-asynchronous-io-with-asp-net-web-api

https://web.archive.org/web/20160718142900/http://blogs.msdn.com:80/b/pfxteam/archive/2011/01/13/10115163.aspx

https://scalablenotions.wordpress.com/2015/05/02/tpl-and-async-await-best-practices-for-the-busy-developer/

class Program
{
    public class Member
    {
        public string N { get; set; }
        public string E { get; set; }
    }


    static void Main()
    {
        List<Member> list = new List<Member>();
        Task.Run(async () => { await SubscribeMembersusingAPI(list); }).GetAwaiter().GetResult();

    }

    // Define other methods and classes here
    public static async Task SubscribeMembersusingAPI(List<Member> members)
    {
        List<string> updateemailstosubscribed = new List<string>();
        HttpClient client = new HttpClient();

        try
        {
            await Task.WhenAll(members.Select(m => SubscribeusingAPI(client, m)));
        }
        catch (Exception e)
        {
            //logger.Error("Error in Async task", e);
        }
        return;
    }

    public static async Task<bool> SubscribeusingAPI(HttpClient client, Member member)
    {
        try
        {
            List<KeyValuePair<String, String>> listkvp = new List<KeyValuePair<string, string>>();
            listkvp.Add(new KeyValuePair<string, string>("e", member.E.Trim()));
            listkvp.Add(new KeyValuePair<string, string>("n", member.N.Trim()));
            listkvp.Add(new KeyValuePair<string, string>("boolean", "true"));
            FormUrlEncodedContent formpost = new FormUrlEncodedContent(listkvp);
            var response = await client.PostAsync("", formpost);
            string responsebody = await response.Content.ReadAsStringAsync();
            if (responsebody == "1" || responsebody == "Member present")
                return true;
            else
                Console.WriteLine(string.Format("FAIL, Error while subscribing e: {0}, n: {1}, response: {2}", member.E, member.N, responsebody));
        }
        catch (Exception e)
        {
            Console.WriteLine(string.Format("FAIL, Error while subscribing e: {0}, n: {1} ", member.E, member.N), e);
        }
        return false;
    }
}
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Let’s try to apply SRP, and get rid of mutable state to increase code maintainability. It inflates C# code a little bit, but usually makes sense.

Demo:

  IEnumerable<Member> members = new Member[] { new Member("n","e") };
  await members
      .SubscribeAsync()
      .LogAsync(Console.Out);

Where Member could be immutable:

public class Member
{
    public Member(string n, string e)
    {
        N = n.Trim();
        E = e.Trim();
    }

    public string N { get; }
    public string E { get; }
    public override string ToString() => $"N={N}, E={E}";
}

We will use this universal helper class to log task exceptions:

static class Logging
{
    public static async Task LogAsync(this Task task, TextWriter writer)
    {
        try
        {
            await task;
        }            
        catch (AggregateException ex)
        {
            foreach (var exx in ex.Flatten().InnerExceptions)
                await writer.WriteLineAsync(exx.Message);
        }
        catch(Exception ex)
        {
            await writer.WriteLineAsync(ex.Message);
        }
    }
}

Now subscription helpers:

static class Subscription
{
    public static Task SubscribeAsync(this IEnumerable<Member> members) =>
        Task.WhenAll(from member in members
                     select member.SubscribeAsync());

    public static Task<SubscriptionResponse> SubscribeAsync(this Member member) =>
        new SubscriptionRequest(member).ExecuteAsync();
}

Subscription request:

public class SubscriptionRequest
{
    const string RequestUrl = "";

    public SubscriptionRequest(Member member)
    {
        Member = member;
    }

    public async Task<SubscriptionResponse> ExecuteAsync()
    {
        using (var client = new HttpClient())
            try
            {
                var response = await client.PostAsync(RequestUrl, Content);
                var responseBody = await response.Content.ReadAsStringAsync();
                return new SubscriptionResponse(responseBody);
            }
            catch(Exception ex)
            {
                throw new SubscriptionException(Member, ex);
            }            
    }

    FormUrlEncodedContent Content => new FormUrlEncodedContent(Kvps);
    IEnumerable<KeyValuePair<string, string>> Kvps => new[]
    {
        new KeyValuePair<string, string>("e", Member.E),
        new KeyValuePair<string, string>("n", Member.N),
        new KeyValuePair<string, string>("boolean", "true")
    };

    Member Member { get; } 
}

And response:

public class SubscriptionResponse
{
    public SubscriptionResponse(string body)
    {
        Body = body;
        if (!Success)
            throw new InvalidOperationException(body);
    }

    public bool Subscribed => Body == "1";
    public bool AlreadySubscribed => Body == "Member present";
    bool Success => Subscribed || AlreadySubscribed;
    string Body { get; }
}

The only missing part:

public class SubscriptionException : Exception
{
    public SubscriptionException(Member member, Exception error) :
        base($"{member} subscription failed, error: {error.Message}", error)
    {
    }
}

P.S. Generally speaking, C# is a pretty verbose language, so it is often punishing good programming practices…

\$\endgroup\$
3
  • \$\begingroup\$ Thats really a good structure, liked the abstraction and helper methods. Appreciate it. Could you also comment more specifically on async/await, et al. \$\endgroup\$
    – user_v
    Jun 11, 2016 at 13:39
  • \$\begingroup\$ I would use AsyncContext from Nito.AsyncEx NuGet package - it will take care about many things for you. Concurrency in C# Cookbook discusses it. \$\endgroup\$ Jun 11, 2016 at 14:54
  • \$\begingroup\$ Had read about it, but since it needed a seperate binary felt using from what is available, can give it a try and see. Thanks \$\endgroup\$
    – user_v
    Jun 11, 2016 at 18:40
0
\$\begingroup\$

Here is a short example why Nito AsyncEx could be useful. Let’s try execute the following code:

    static Task MainAsync(string[] args)
    {
        return Task.WhenAll(from m in Enumerable.Range(0, 10)
                            select PrintAsync(m));
    }

Where:

    static async Task PrintAsync(int m)
    {
        var rnd = new Random();
        await Task.Delay(rnd.Next(10));
        await Console.Out.WriteLineAsync(m.ToString());
        await Console.Out.WriteLineAsync((m+100).ToString());
    }

With AsyncContext:

    static void Main(string[] args)
    {
       AsyncContext.Run(() => MainAsync(args));
    }

enter image description here

And without AsyncContext (can vary from run to run):

    static void Main(string[] args)
    {
        MainAsync(args).GetAwaiter().GetResult();
    }

enter image description here

I could not say that understand all the mechanics here, please correct me if I am wrong.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.