Joe Riggs
Joe Riggs

Reputation: 1324

Create generic async task function

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;
}

Upvotes: 2

Views: 6004

Answers (2)

Christos
Christos

Reputation: 53958

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;
}

Upvotes: 6

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149538

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");
}

Upvotes: 2

Related Questions