35

I am writing a very, very simple query which just gets a document from a collection according to its unique Id. After some frusteration (I am new to mongo and the async / await programming model), I figured this out:

IMongoCollection<TModel> collection = // ...
FindOptions<TModel> options = new FindOptions<TModel> { Limit = 1 };
IAsyncCursor<TModel> task = await collection.FindAsync(x => x.Id.Equals(id), options);
List<TModel> list = await task.ToListAsync();
TModel result = list.FirstOrDefault();
return result;

It works, great! But I keep seeing references to a "Find" method, and I worked this out:

IMongoCollection<TModel> collection = // ...
IFindFluent<TModel, TModel> findFluent = collection.Find(x => x.Id == id);
findFluent = findFluent.Limit(1);
TModel result = await findFluent.FirstOrDefaultAsync();
return result;

As it turns out, this too works, great!

I'm sure that there's some important reason that we have two different ways to achieve these results. What is the difference between these methodologies, and why should I choose one over the other?

object88
  • 720
  • 1
  • 7
  • 20
  • 1
    The `async` keyword lets the compiler "manage" your code via its scheduler. This can but won't always make your app multi-threaded. The `await` keyword signals to the compiler good points to decide and switch context or utilize a new thread. – whoisj Jun 04 '15 at 21:02
  • The `async` specifically does _not_ make your app multithreaded. See [here](https://msdn.microsoft.com/en-us/library/hh191443.aspx). This isn't a question about C#, but the differences in methods available from the MongoDB C# API. – object88 Jun 04 '15 at 21:46
  • What do you mean that `FindAsync` won't make your app multi-threaded? There's a good chance it will do that if you don't `await` right away. – John Saunders Jun 05 '15 at 01:22
  • 3
    No, if you read the link I posted, you will see that using `async` and `await` will explicitly _not_ create threads. It does not create a background thread, nor does it put a thread to sleep. The whole point of `async` and `await` is to relinquish control of a thread such that an message loop can process another queued up message on the same thread. If what you are `await`ing does not itself run on another thread or perform a background request intrinsically, adding `await` will not cause it to. The anti-example for using `await` is a CPU-bound function. – object88 Jun 05 '15 at 12:58

2 Answers2

55

The difference is in a syntax. Find and FindAsync both allows to build asynchronous query with the same performance, only

FindAsync returns cursor which doesn't load all documents at once and provides you interface to retrieve documents one by one from DB cursor. It's helpful in case when query result is huge.

Find provides you more simple syntax through method ToListAsync where it inside retrieves documents from cursor and returns all documents at once.

rnofenko
  • 9,198
  • 2
  • 46
  • 56
  • So, in my particular case, there is no effective difference, as I am loading exactly one document either way. In the case of a large batch of documents, it _sounds_ like I would want to use the cursor (and perhaps not follow it up with `ToListAsync`, as that would then load all the documents?). Is that a fair assessment? – object88 Jun 05 '15 at 13:07
  • 1
    For one document Find looks better because you doesn't work with cursor. – rnofenko Jun 05 '15 at 18:17
  • I understand your answer and I'm curious if the described difference has to do with the fact that the options' object accepted is generic in the former case (i.e. `FindAsync(...,options: FindOptions _)` and non-generic in the latter (i.e. `Find(...,options: FindOptions _)`. In my case, it makes a lot of difference because the generic one has a field for projections and the other doesn't. – Konrad Viltersten Jun 20 '21 at 18:19
  • In addition to that, when using `FindAsync`, after executing it, the query was already sent to the database, while `Find` doesn't send it immediately, just when you try to enumerate the results (ex.: using `ToListAsync`). – dandev486 Feb 11 '22 at 21:56
0

Imagine that you execute this code in a web request, with invoking find method the thread of the request will be frozen until the database return results it's a synchron call, if it's a long database operation that takes seconds to complete, you will have one of the threads available to serve web request doing nothing simply waiting that database return the results, and wasting valuable resources (the number of threads in thread pool is limited).

With FindAsync, the thread of your web request will be free while is waiting the database for returning the results, this means that during the database call this thread is free to attend an another web request. When the database returns the result then the code continue execution.

For long operations like read/writes from file system, database operations, comunicate with another services, it's a good idea to use async calls. Because while you are waiting for the results, the threads are available for serve another web request. This is more scalable.

Take a look to this microsoft article https://msdn.microsoft.com/en-us/magazine/dn802603.aspx.

Marc Cals
  • 2,963
  • 4
  • 30
  • 48
  • 2
    Both pieces of code are async, though. In the first example, there are two awaits: one on the FindAsync and one on the ToListAsync. In the second, there is just one await: on the FirstOrDefaultAsync. What I would like to know is not so much what the benefits of async / await are (I get that), but rather, what are the potentially blocking parts of the different code paths? Are there circumstances where it's better to use FindAsync & ToListAsync, vs FirstOrDefaultAsync? – object88 Jun 04 '15 at 19:45
  • Is it working this code? With find is not necessary toList because it just returns the first object it finds https://msdn.microsoft.com/en-us/library/x0b5b5bc%28v=vs.110%29.aspx – Marc Cals Jun 04 '15 at 19:51
  • I expanded out the code a little to clarify the question. – object88 Jun 04 '15 at 19:54
  • Sorry -- Collection isn't a System.Collections.Generic.List object. Clarified further... – object88 Jun 04 '15 at 19:56