Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. How to write a generic code to make async call to Rest Service

How to write a generic code to make async call to Rest Service

Scheduled Pinned Locked Moved C#
tutorialmobiledatabasesqlitedebugging
14 Posts 4 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A ArunHanu

    Thank you Benjamin, Even making the change will create a strong bond with todoitem. My intention is to make it generalised so that I can make a single method for making calls for different URL's and get data for different class object As I have stated earlier I don't want to write the same code for a different URL request.

    N Offline
    N Offline
    Nathan Minier
    wrote on last edited by
    #5

    Not a Benjamin, that's a quote. You need to tack down the real basic fundamentals first; generics in C# are first class members and almost trivial. But if you'd rather walk before crawling:

    public async T RefreshData(string url)
    {
    try
    {
    var response = await client.GetAsync (url);
    if (response.IsSuccessStatusCode) {
    var content = await response.Content.ReadAsStringAsync ();
    var items = JsonConvert.DeserializeObject<T>(content);
    if(items != null)
    {
    return items;
    }
    throw new Exception($"Service did not provide a response formatted as type: {typeof(T)}");
    }else{
    throw new Exception($"Service request failure. Code: {response.StatusCode}");
    }
    }
    catch (Exception ex)
    {
    Debug.WriteLine ($"ERROR {ex.Message}");
    }
    }

    The $"" is the newfangled string interpolation BTW. Call with:

    var myList = RefreshData>("http ://localhost:9054/TotoItem");

    I had to throw an extra space in the string as the C# preformat is throwing in an anchor tag.

    "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

    1 Reply Last reply
    0
    • N Nathan Minier

      Well, you're returning List, which is different from Task>. It's also worth noting that your method, as written, will run synchronously despite your *Async() naming. Convert the return type to List and rename your function and see what happens. You likely also want to define variables in the method scope for Items and RestUrl. You also appear to have a typo in a string constant. Check the RestUrl variable.

      "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

      Richard DeemingR Offline
      Richard DeemingR Offline
      Richard Deeming
      wrote on last edited by
      #6

      Nathan Minier wrote:

      you're returning List<todoitem>, which is different from Task<List<todoitem>>.

      It's an async method, so that's perfectly normal. :)

      Nathan Minier wrote:

      your method, as written, will run synchronously

      Nope:

      var response = await client.GetAsync (uri);


      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

      "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

      A N 2 Replies Last reply
      0
      • Richard DeemingR Richard Deeming

        Nathan Minier wrote:

        you're returning List<todoitem>, which is different from Task<List<todoitem>>.

        It's an async method, so that's perfectly normal. :)

        Nathan Minier wrote:

        your method, as written, will run synchronously

        Nope:

        var response = await client.GetAsync (uri);


        "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

        A Offline
        A Offline
        ArunHanu
        wrote on last edited by
        #7

        Thanks a lot Nathan Minier and Richard Deeming for the solution. I will try to implement same solution and check

        1 Reply Last reply
        0
        • Richard DeemingR Richard Deeming

          Nathan Minier wrote:

          you're returning List<todoitem>, which is different from Task<List<todoitem>>.

          It's an async method, so that's perfectly normal. :)

          Nathan Minier wrote:

          your method, as written, will run synchronously

          Nope:

          var response = await client.GetAsync (uri);


          "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

          N Offline
          N Offline
          Nathan Minier
          wrote on last edited by
          #8

          I'm sorry Richard, that's not correct. Using the async keyword can mean that you want to return a Task that is not yet resolved, but doesn't necessarily mean that. It can simply mean that you're coalescing an asynchronous operation into a synchronous context, which is precisely what "await" does. Using the await keyword means that you want to use the value provided by Task.Result, not the Task itself. It blocks execution of the method (ie runs synchronously) until the awaited Task is completed. That means that while the method is flagged as async, it resolves async operations into a synchronous context. I understand that the terminology is a bit misleading and the documentation does not help. But the fact is that if you're blocking the thread, you are running synchronously. If the method were run asynchronously, it would look more like this:

          public async Task RefreshData(string url)
          {
          return Task.Run(()=>
          {
          var response = await client.GetAsync (url);
          if (response.IsSuccessStatusCode) {
          var content = await response.Content.ReadAsStringAsync ();
          var items = JsonConvert.DeserializeObject(content);
          if(items != null)
          {
          return items;
          }
          throw new Exception($"Service did not provide a response formatted as type: {typeof(T)}");
          }else{
          throw new Exception($"Service request failure. Code: {response.StatusCode}");
          }
          })
          .ContinueWith(ex => { Debug.WriteLine ($"ERROR {ex.Message}");}, TaskContinuationOptions.OnlyOnFaulted));
          }

          I just didn't want to confuse the poor guy with Task execution and moving between synchronous and asynchronous contexts when he doesn't even have generics down.

          "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

          Richard DeemingR 1 Reply Last reply
          0
          • N Nathan Minier

            I'm sorry Richard, that's not correct. Using the async keyword can mean that you want to return a Task that is not yet resolved, but doesn't necessarily mean that. It can simply mean that you're coalescing an asynchronous operation into a synchronous context, which is precisely what "await" does. Using the await keyword means that you want to use the value provided by Task.Result, not the Task itself. It blocks execution of the method (ie runs synchronously) until the awaited Task is completed. That means that while the method is flagged as async, it resolves async operations into a synchronous context. I understand that the terminology is a bit misleading and the documentation does not help. But the fact is that if you're blocking the thread, you are running synchronously. If the method were run asynchronously, it would look more like this:

            public async Task RefreshData(string url)
            {
            return Task.Run(()=>
            {
            var response = await client.GetAsync (url);
            if (response.IsSuccessStatusCode) {
            var content = await response.Content.ReadAsStringAsync ();
            var items = JsonConvert.DeserializeObject(content);
            if(items != null)
            {
            return items;
            }
            throw new Exception($"Service did not provide a response formatted as type: {typeof(T)}");
            }else{
            throw new Exception($"Service request failure. Code: {response.StatusCode}");
            }
            })
            .ContinueWith(ex => { Debug.WriteLine ($"ERROR {ex.Message}");}, TaskContinuationOptions.OnlyOnFaulted));
            }

            I just didn't want to confuse the poor guy with Task execution and moving between synchronous and asynchronous contexts when he doesn't even have generics down.

            "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

            Richard DeemingR Offline
            Richard DeemingR Offline
            Richard Deeming
            wrote on last edited by
            #9

            No, sorry, that's not correct. The await client.GetAsync(url) and await response.Content.ReadAsStringAsync() calls will not block the current thread. They will sign up the rest of the method as a continuation, which will be invoked when the returned Task<T> has completed. The GetAsync and ReadAsStringAsync calls (eventually) use an IO completion port to receive notification when the request has completed. That's the whole point of async / await. :) The only thing your example is doing differently is that it's using a background thread to run the very first part of the method - the call to GetAsync. As a result, the rest of the method will not run on the captured execution context from the caller of RefreshData. But you could achieve the same result by adding .ConfigureAwait(false) to the end of the GetAsync and ReadAsStringAsync calls.


            "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

            "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

            N 1 Reply Last reply
            0
            • Richard DeemingR Richard Deeming

              No, sorry, that's not correct. The await client.GetAsync(url) and await response.Content.ReadAsStringAsync() calls will not block the current thread. They will sign up the rest of the method as a continuation, which will be invoked when the returned Task<T> has completed. The GetAsync and ReadAsStringAsync calls (eventually) use an IO completion port to receive notification when the request has completed. That's the whole point of async / await. :) The only thing your example is doing differently is that it's using a background thread to run the very first part of the method - the call to GetAsync. As a result, the rest of the method will not run on the captured execution context from the caller of RefreshData. But you could achieve the same result by adding .ConfigureAwait(false) to the end of the GetAsync and ReadAsStringAsync calls.


              "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

              N Offline
              N Offline
              Nathan Minier
              wrote on last edited by
              #10

              I was completely lazy with that sample and I accept that, but it was just to illustrate a point. I think the issue we're having is that we're discussing different contexts. I'm talking about the code that I'm looking at. I can't see the calling code, so am therefore not addressing it, and that's the code you're talking about. I realize that internal conventions are not the same as generally accepted practice, and my point about the return type and naming convention was not relevant, so I accept proper chastisement there. I think we can both agree that the code we're looking at, and just that code, will not behave in an asynchronous manner.

              "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

              Richard DeemingR 1 Reply Last reply
              0
              • N Nathan Minier

                I was completely lazy with that sample and I accept that, but it was just to illustrate a point. I think the issue we're having is that we're discussing different contexts. I'm talking about the code that I'm looking at. I can't see the calling code, so am therefore not addressing it, and that's the code you're talking about. I realize that internal conventions are not the same as generally accepted practice, and my point about the return type and naming convention was not relevant, so I accept proper chastisement there. I think we can both agree that the code we're looking at, and just that code, will not behave in an asynchronous manner.

                "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

                Richard DeemingR Offline
                Richard DeemingR Offline
                Richard Deeming
                wrote on last edited by
                #11

                But that's the point - it WILL behave in an asynchronous manner! :)

                public async Task<List<todoitem>> RefreshDataAsync ()
                {
                Items = new List<todoitem> ();
                RestUrl = "http://localhost:9054/TotoItem"
                var uri = new Uri (string.Format (RestUrl, string.Empty));

                try {
                    // Runs synchronously up to here...
                    var response = await client.GetAsync (uri);
                    
                    // The rest of the method is signed up as a continuation,
                    // invoked when the IO completion port notifies that the request has been completed.
                    // The current thread is NOT blocked.
                    
                    // Depending on the calling context, the continuation MIGHT run on the same thread as the caller.
                    // But this is handled asynchronously; the thread does not block waiting to run the continuation.
                    // This can be changed by adding ".ConfigureAwait(false)" after "GetAsync(url)".
                    
                    if (response.IsSuccessStatusCode) {
                        var content = await response.Content.ReadAsStringAsync ();
                        // Again, the rest of the method is signed up as a continuation.
                        // The thread is NOT blocked waiting for the response content to be downloaded.
                        
                        Items = JsonConvert.DeserializeObject <List<todoitem>> (content);
                        // The JSON conversion IS run syncronously.
                        // But async support in JSON.NET was only recently added (v10.1), so there's probably no option here.
                }
                } catch (Exception ex) {
                    Debug.WriteLine (@" ERROR {0}", ex.Message);
                }
                
                return Items;
                

                }


                "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

                N 1 Reply Last reply
                0
                • Richard DeemingR Richard Deeming

                  But that's the point - it WILL behave in an asynchronous manner! :)

                  public async Task<List<todoitem>> RefreshDataAsync ()
                  {
                  Items = new List<todoitem> ();
                  RestUrl = "http://localhost:9054/TotoItem"
                  var uri = new Uri (string.Format (RestUrl, string.Empty));

                  try {
                      // Runs synchronously up to here...
                      var response = await client.GetAsync (uri);
                      
                      // The rest of the method is signed up as a continuation,
                      // invoked when the IO completion port notifies that the request has been completed.
                      // The current thread is NOT blocked.
                      
                      // Depending on the calling context, the continuation MIGHT run on the same thread as the caller.
                      // But this is handled asynchronously; the thread does not block waiting to run the continuation.
                      // This can be changed by adding ".ConfigureAwait(false)" after "GetAsync(url)".
                      
                      if (response.IsSuccessStatusCode) {
                          var content = await response.Content.ReadAsStringAsync ();
                          // Again, the rest of the method is signed up as a continuation.
                          // The thread is NOT blocked waiting for the response content to be downloaded.
                          
                          Items = JsonConvert.DeserializeObject <List<todoitem>> (content);
                          // The JSON conversion IS run syncronously.
                          // But async support in JSON.NET was only recently added (v10.1), so there's probably no option here.
                  }
                  } catch (Exception ex) {
                      Debug.WriteLine (@" ERROR {0}", ex.Message);
                  }
                  
                  return Items;
                  

                  }


                  "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                  N Offline
                  N Offline
                  Nathan Minier
                  wrote on last edited by
                  #12

                  var response = await client.GetAsync (uri);
                  // Code execution within this method halted until response is resolved
                  if (response.IsSuccessStatusCode) {

                  This is an synchronous pattern within the context of the method, which is the only code that we see. No other actions will be taken by the available code until this resolves. The lack of thread blocking is not the point, the code execution flow in the method is the same as if the thread were blocked. The Task continuation is only of interest to the calling code so that it can treat it appropriately.

                  "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

                  Richard DeemingR 1 Reply Last reply
                  0
                  • N Nathan Minier

                    var response = await client.GetAsync (uri);
                    // Code execution within this method halted until response is resolved
                    if (response.IsSuccessStatusCode) {

                    This is an synchronous pattern within the context of the method, which is the only code that we see. No other actions will be taken by the available code until this resolves. The lack of thread blocking is not the point, the code execution flow in the method is the same as if the thread were blocked. The Task continuation is only of interest to the calling code so that it can treat it appropriately.

                    "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

                    Richard DeemingR Offline
                    Richard DeemingR Offline
                    Richard Deeming
                    wrote on last edited by
                    #13

                    The same argument applies to your "asynchronous" version:

                    public async Task<T> RefreshData<T>(string url)
                    {
                    return Task<T>.Run(()=>
                    {
                    var response = await client.GetAsync (url);
                    // Code execution within this method halted until response is resolved
                    ...
                    })
                    .ContinueWith(ex => { Debug.WriteLine ($"ERROR {ex.Message}");}, TaskContinuationOptions.OnlyOnFaulted));
                    }

                    You've just moved the "logically synchronous" part to a lambda method, at the expense of making the outer method more complicated. :) Adding the *Async suffix to a task-returning method is the recommended naming convention. Adding an extra Task.Run for extra asynchronicity doesn't really achieve anything. It's only going to have any effect if your method has with a long-running synchronous block before the first await call, and Task.Yield would probably be a better solution for that.


                    "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                    "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

                    N 1 Reply Last reply
                    0
                    • Richard DeemingR Richard Deeming

                      The same argument applies to your "asynchronous" version:

                      public async Task<T> RefreshData<T>(string url)
                      {
                      return Task<T>.Run(()=>
                      {
                      var response = await client.GetAsync (url);
                      // Code execution within this method halted until response is resolved
                      ...
                      })
                      .ContinueWith(ex => { Debug.WriteLine ($"ERROR {ex.Message}");}, TaskContinuationOptions.OnlyOnFaulted));
                      }

                      You've just moved the "logically synchronous" part to a lambda method, at the expense of making the outer method more complicated. :) Adding the *Async suffix to a task-returning method is the recommended naming convention. Adding an extra Task.Run for extra asynchronicity doesn't really achieve anything. It's only going to have any effect if your method has with a long-running synchronous block before the first await call, and Task.Yield would probably be a better solution for that.


                      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                      N Offline
                      N Offline
                      Nathan Minier
                      wrote on last edited by
                      #14

                      As I said: it was a bad, rushed example. I will continue to accept bads on that.

                      "There are three kinds of lies: lies, damned lies and statistics." - Benjamin Disraeli

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups