That is explained in the link you referenced:
documentation wrote:
The code above will make the compiler wrap all code after the await into a continuation of the task being awaited and will execute that after that task is finished, which makes the code below the await avoid re-entering the adapter during the async task.
This is equal to re-using an adapter instance in a multi-threaded environment: it's not thread safe and calling an async method doesn't free you from taking care of this: calling an async method could create multi-threading related issues with an adapter instance if you're not careful.
This also means that if you share an IDataAccessAdapter across methods on a form for example, you can have re-entrancy problems if you have async event handlers. In this case, create a new DataAccessAdapter instance every time you need one (e.g. one per method). Creating a new DataAccessAdapter is very fast (The constructor is almost completely empty) so it doesn't make your code slower.
Rule of thumb: Always await a Task returned by an async method before doing anything else on the adapter.
You have to await directly on the async task, in this case:
var query1 = await db.EnrollmentAccess.Where(ea=>ea.Date == date).ToListAsync();
var query2 = await db.LearnerCompletion.Where(ea=>ea.Date == date).ToListAsync();
To do what you expect, you should use another DataAccessAdapter instance in the second call (thus another LinqMetaData that uses another adapter instance), as you would do in a multi-thread environment, as it's not thread safe to use the same adapter in multiple threads. Like:
var query1 = new LinqMetaData(adapter1).EnrollmentAccess.Where(ea=>ea.Date == date).ToListAsync();
var query2 = new LinqMetaData(adapter2).LearnerCompletion.Where(ea=>ea.Date == date).ToListAsync();
await query1;
await query2;