How To Fix The FeedIterator.ReadNextAsync Response Not Returning


The creation of a Cosmos DB instance within the Azure subscription is beyond this blog. The following details assume you already have an Azure subscription with a Cosmos DB instance and you are experiencing an issue with the ReadNextAsync() is not returning expected results.
A partial class diagram of the Web API that is used to interact with the Cosmos DB is shown in Figure 1.
How To Fix The FeedIterator.ReadNextAsync Response Not Returning 
Figure 1 - Class diagram view of the Login functionality for the Web API that interacts with the CosmosDB service.
The flow of execution with the messages and return information is in Figure 2.
How To Fix The FeedIterator.ReadNextAsync Response Not Returning
Figure 2 - Activity diagram of the Login functionality that uses the Cosmos DB to return User information.
For the purposes of demonstration, code snippet in Listing 1 is the class constructor of the CosmosDBService class. The constructor sets the specific Cosmos DB instance that will be used to validate the login and gets the user details if the login in successful.
  1. public CosmosDBService(String connection, String dbasename, String containername) {  
  2.     this.dbclient = new CosmosClient(connection);  
  3.     this.databasename = dbasename;  
  4.     this.containername = containername;  
  5.     this.container = this.dbclient.GetContainer(this.databasename, this.containername);  
  6. }  
Listing 1 CosmosDBService class constructor code.
With the class constructor in place and the class initialized, the call to LoginUser is then made passing the provided email and password login credentials to verify. Listing 2 is the first part of the LoginUser method in the CosmosDBService that demonstrates how to use a QueryDefinition with parameters to get validate the user’s access to the application.
  1. public async Task < UserDetails > LoginUser(Login login) {  
  2.         try {  
  3.             String sqlQueryText = "SELECT * FROM c WHERE = @email AND c.password=@password";  
  4.             QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText);  
  5.             queryDefinition.WithParameter("@email", (String) login.Email);  
  6.             queryDefinition.WithParameter("@password", (String) login.Password);  
  7.             FeedIterator < UserDetails > queryResultSetIterator = this.container.GetItemQueryIterator < UserDetails > (queryDefinition, requestOptions: new QueryRequestOptions {  
  8.                 MaxConcurrency = 1  
  9.             });  
  10.             while (queryResultSetIterator.HasMoreResults) {  
  11.                 …  
Listing 2 Code snippet of the CosmosDBService.LoginUser method.

Traversing the Results the Proper Way – Is it a Microsoft bug?

Up to this point, the code is working as expected – The connection to Cosmos DB is established and the container that is storing the login credentials has been accessed successfully with the QueryDefinition. According to the Microsoft Azure SDK documentation, the next step is to call the ReadNextAsync() preceded by the await command because the call is being made asynchronously by reaching back to the container to pull down the next item. Listing 3 is an example from the Microsoft Azure SDK documentation.
  1. while (feedIterator.HasMoreResults) {  
  2.     FeedResponse < MyItem > response = await feedIterator.ReadNextAsync();  
  3.     foreach(var item in response) {  
  4.         Console.WriteLine(item);  
  5.     }  
  6. }  
Listing 3 is Microsoft's example of using the await command for the ReadNexAsync() method of the FeedIterator object.
Here is where the issue begins. Using the await command to wait for the ReadNexAsync() method to finish results in a lost response to the request. When debugging through the code and placing a breakpoint on the while statement and then another on the foreach statement, the foreach statement is never reached because the response does not return.

The Fix to the await ReadnexAsync() Issue

To resolve the issue, you have to return to the good ole days of manually setting up a Task construct and allowing the Task construct to manage the requested task for you. The await functionality is a nice short-cut that is supposed to wrap the Task constructor for you, however, I have found it is not always reliable. Listing 4 is the complete code snippet for the CosmosDBService LoginUser method that uses the Task method directly that calls the wait method directly. And now the code can continue execution without being blocked by the await command.
  1. public async Task < UserDetails > LoginUser(Login login) {  
  2.     try {  
  3.         String sqlQueryText = "SELECT * FROM c WHERE = @email AND c.password=@password";  
  4.         QueryDefinition queryDefinition = new QueryDefinition(sqlQueryText);  
  5.         queryDefinition.WithParameter("@email", (String) login.Email);  
  6.         queryDefinition.WithParameter("@password", (String) login.Password);  
  7.         FeedIterator < UserDetails > queryResultSetIterator = this.container.GetItemQueryIterator < UserDetails > (queryDefinition, requestOptions: new QueryRequestOptions {  
  8.             MaxConcurrency = 1  
  9.         });  
  10.         while (queryResultSetIterator.HasMoreResults) {  
  11.             var task = queryResultSetIterator.ReadNextAsync();  
  12.             task.Wait();  
  13.             FeedResponse < UserDetails > currentResultSet = task.Result;  
  14.             if (currentResultSet.Count == 1) {  
  15.                 return currentResultSet.FirstOrDefault();  
  16.             }  
  17.         }  
  18.         UserDetails userresponse = new UserDetails();  
  19.         userresponse.ErrorMessage = "INVALID LOGIN";  
  20.         userresponse.Id = "0";  
  21.         return userresponse;  
  22.     } catch (Exception ex) {  
  23.         UserDetails userresponse = new UserDetails();  
  24.         userresponse.ErrorMessage = "ERROR - " + ex.Message;  
  25.         userresponse.Id = "-1";  
  26.         return userresponse;  
  27.     }  
  28. }  
Listing 4 - LoginUser method of the CosmosDBService class that replaces the await command with the manual instantiate of a Task construct as a workaround to the apparent defect in the ReadNextAsync() call.