2

I have created a function that returns an object using async/await. I would like to make the function generic so that it can return whatever object that I pass in. The code is boilerplate except for the objects being returned. I would like to be able to call GetAsync and have it return the correct object

public Patron getPatronById(string barcode)
{
    string uri = "patrons/find?barcode=" + barcode;
    Patron Patron =  GetAsync(uri).Result;
    return Patron;
}

private async Task<Patron> GetAsync(string uri)
{
    var client = GetHttpClient(uri);
    var content = await client.GetStringAsync(uri);
    JavaScriptSerializer ser = new JavaScriptSerializer();
    Patron Patron = ser.Deserialize<Patron>(content);
    return Patron;
}
Christos
  • 53,228
  • 8
  • 76
  • 108
Joe Riggs
  • 1,312
  • 4
  • 23
  • 48
  • object is root object? https://msdn.microsoft.com/en-us/library/system.object(v=vs.110).aspx – online Thomas Aug 30 '16 at 19:32
  • You shouldn't use `.Result` on the result of `GetAsync`. If you call it on the UI thread, you'll get a deadlock. – Thomas Levesque Aug 30 '16 at 19:34
  • 2
    Why is `getPatronById` not async? It should either be async and you don't use `.Result` or `GetAsync` should not be async and you should use `WebClient` instead of `HttpClient` and use it's syncronous methods. Using `.Result` will likely cause your program to lock up. – Scott Chamberlain Aug 30 '16 at 19:35
  • Also a `.ConfigureAwait(false)` on the `.GetStringAsync(uri)` would not hurt either. – Scott Chamberlain Aug 30 '16 at 19:37

2 Answers2

6

What about a generic method?

private async Task<T> GetAsync<T>(string uri)
{
    var client = GetHttpClient(uri);
    var content = await client.GetStringAsync(uri);
    var serializer = new JavaScriptSerializer();
    var t = serializer.Deserialize<T>(content);
    return t;
}

Normally, you should place this method into another class and make it public, in order it can be used by methods in different classes.

Regarding the way you call this method, you could try the following:

 // I capitalized the first letter of the method, 
 // since this is a very common convention in .NET
 public Patron GetPatronById(string barcode)
 {
     string uri = "patrons/find?barcode=" + barcode;
     var Patron =  GetAsync<Patron>(uri).Result;
     return Patron;
 }

Note: In the above snippet I assumed that you haven't moved the GetAsync into another class. If you move it, then you have to make a slight change.

Update

I'm not following what you mean by your note. Do I need to make GetPatronById a task function as well - like Yuval has done below?

I mean something like this:

// The name of the class may be not the most suitable in this case.
public class Repo
{
    public static async Task<T> GetAsync<T>(string uri)
    {
        var client = GetHttpClient(uri);
        var content = await client.GetStringAsync(uri);
        var serializer = new JavaScriptSerializer();
        var t = serializer.Deserialize<T>(content);
        return t;
    }
}

public Patron GetPatronById(string barcode)
{
     string uri = "patrons/find?barcode=" + barcode;
     var Patron =  Repo.GetAsync<Patron>(uri).Result;
     return Patron;
}
Christos
  • 53,228
  • 8
  • 76
  • 108
  • I'm not following what you mean by your note. Do I need to make GetPatronById a task function as well - like Yuval has done below? – Joe Riggs Aug 30 '16 at 20:01
2

Generic can be easily done with:

private async Task<T> GetAsync(string uri)
{
    var client = GetHttpClient(uri);
    var content = await client.GetStringAsync(uri);
    return JsonConvert.DeserializeObject<T>(content);
}

Things to note:

  1. JavaScriptSerializer has been deprecated for ages, avoid using it. Try out Json.NET instead.

  2. This:

    Patron Patron =  GetAsync(uri).Result;
    

    is dangerous and can cause potential deadlocks, especially in Web API. You need to go "async all the way":

    public Task<Patron> GetPatronByIdAsync(string barcode)
    {
       string uri = $"patrons/find?barcode={barcode}";
       return GetAsync<Patron>(uri);
    }
    

And only your top most level invoker need await on the Task. Possibly some controller action:

public async Task SomeAction()
{
     await GetPatronByIdAsync("hello");
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321