The DocumentClient class has ReadDocumentAsync and CreateDocumentAsync, but lacks CreateDocumentQueryAsync.
As a workaround, I'm wrapping CreateDocumentQuery in Task.Run to ensure that it doesn't block the main thread.
Example
readonly DocumentClient client = new DocumentClient(new Uri(DocumentDbConstants.Url), DocumentDbConstants.PrimaryKey);
public Task<List<PersonModel>> GetAllPersonModels()
{
return Task.Run(() => client.CreateDocumentQuery<PersonModel>(_documentCollectionUri).ToList());
}
@brminnick We do have async version of the Query APIs that you can use to run the queries in async fashion life other CRUD APIs.
Note that instantiating CreateDocumentQuery doesn't executes any query. It's the calling of ToList in above example that executes the query. This is the blocking way for executing the query. If you want to execute the query in async fashion, use the following code:
var personModels = new List<PersonModel>();
var queryable = client.CreateDocumentQuery<PersonModel>(_documentCollectionUri, query).AsDocumentQuery();
while(queryable.HasMoreResults)
{
var response = await queryable.ExecuteNextAsync<PersonModel>();
personModels.AddRange(response);
}
Let me know if that helps!
Thanks @rnagpal
Can we change the name of ToList() to ToListAsync()? If the ToList() method is executing the query and performing an HttpClient task, it should be awaitable and include "Async" in the method name to follow best-practices.
The example above works, but it makes for an inconsistent API. I am new to DocumentDb and NoSQL, but it seems strange that the API is drastically different for Deleting a Document / Creating a Document / Getting One Document versus Getting All Documents (example below).
public static Task<List<PersonModel>> GetAllPersonModelsAsync()
{
return Task.Run(() => client.CreateDocumentQuery<PersonModel>(_documentCollectionUri).ToList());
}
public static async Task<PersonModel> GetPersonModelAsync(string id)
{
var result = await client.ReadDocumentAsync<PersonModel>(CreateDocumentUri(id));
return result;
}
public static async Task<PersonModel> CreatePersonModelAsync(PersonModel person)
{
var result = await client.CreateDocumentAsync(_documentCollectionUri, person);
return (PersonModel)result.Resource;
}
public static async Task<HttpStatusCode> DeletePersonModel(string id)
{
var result = await client.DeleteDocumentAsync(CreateDocumentUri(id));
return result.StatusCode;
}
Similar to @rnagpal comment I've created ToListAsync extensions
public static async Task<List<T>> ToListAsync<T>(this IDocumentQuery<T> queryable)
{
var list = new List<T>();
while (queryable.HasMoreResults)
{ //Note that ExecuteNextAsync can return many records in each call
var response = await queryable.ExecuteNextAsync<T>();
list.AddRange(response);
}
return list;
}
public static async Task<List<T>> ToListAsync<T>(this IQueryable<T> query)
{
return await query.AsDocumentQuery().ToListAsync();
}
How does one do a CountAsync() in this way?
ExecuteNextAsync will return all results (though paged) and if you limit it the Count property will be modified.
Thanks @michael-freidgeim-webjet
We can optimize ToListAsync by removing the async operator and returning the Task. This prevents the overhead that the compiler generates when using async.
public static Task<List<T>> ToListAsync<T>(this IQueryable<T> query) => query.AsDocumentQuery().ToListAsync();
public static async Task<List<T>> ToListAsync<T>(this IDocumentQuery<T> queryable)
{
var list = new List<T>();
while (queryable.HasMoreResults)
{
//Note that ExecuteNextAsync can return many records in each call
var response = await queryable.ExecuteNextAsync<T>();
list.AddRange(response);
}
return list;
}
Any progress on this one? I am curious why there is no async read query for documents.
@brminnick We do have async version of the Query APIs that you can use to run the queries in async fashion life other CRUD APIs.
Note that instantiating CreateDocumentQuery doesn't executes any query. It's the calling of ToList in above example that executes the query. This is the blocking way for executing the query. If you want to execute the query in async fashion, use the following code:
var personModels = new List<PersonModel>(); var queryable = client.CreateDocumentQuery<PersonModel>(_documentCollectionUri, query).AsDocumentQuery(); while(queryable.HasMoreResults) { var response = await queryable.ExecuteNextAsync<PersonModel>(); personModels.AddRange(response); }Let me know if that helps!
It does not work when calling the function without type.
client.CreateDocumentQuery(_documentCollectionUri, query).AsDocumentQuery();
AsDocumentQuery() not exist.
I tried running this code according to your sample and can't get it to work. There's no .Where or .AsEnumerable(), according to your documentation this API is not obsolete? I can't seem to find any working example of how to query a single record with cosmo db SDK for c#. I'm using DocumentDB.Core version 2.2
Sample Code: SalesOrder querySalesOrder = client.CreateDocumentQuery
.Where(so => so.AccountNumber == "10-4020-000510")
.AsEnumerable()
.FirstOrDefault();
Sample Code Source:
(https://github.com/Azure/azure-cosmos-dotnet-v2/blob/f374cc601f4cf08d11c88f0c3fa7dcefaf7ecfe8/samples/code-samples/DocumentManagement/Program.cs#L248-L251) an
Most helpful comment
Any progress on this one? I am curious why there is no async read query for documents.