I am using MongoDB.Driver 2.10.4
I want to get all documents that have an id in a list of ids that I get from my controller
I am doing something like this
var pending_processes = mongo_context.collectionName
.AsQueryable()
.Where(x => request.ids.Contains(x.Id))
.Select(x => new ViewModel()
{
process_id = x.Id,
date = not important just accessing the x object
state = new States()
{
timeline = x.states.timeline,
part = x.states.part,
}
}).ToList();
The code above works fine but if I make my function async and do an await and replace ToList by ToListAsync I get the following error
The source IQueryable doesn't implement IAsyncEnumerable<Application.Process.Query.GetPendingProcesses.ViewModel>. Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
Clearly there is something I am not getting here my concern is I don't want my code to run synchronously this would be really bad. usually when dealing with postgresql context I always use the ToListAsync but here in order to use linq with mongo i had to use AsQueryable() and AsQueryable as I understand it does not get the data it's a normal query that I need to execute afterwards but when I use with it ToList everything works but when I use ToListAsync I get the error.
I just want to know what is behind all of this and is the code above synchronous or asynchronous?
Sure you can keep it asynchronous, but first you have switch out AsQueryable
to some other method which returns back and IQueryable
.
In a nutshell ToListAsync()
works on a IQueryable<T>
only , when you turned it in to a IEnumerable via AsEnumerable() you lost the ability to call it. Its explained well here
You have a couple of choices, either implement IDbAsyncEnumerable see here or change the result list you have into an async list
with Task.FromResult()
Option 1:
// try this in your controller
public async Task<List<PeopleStatesType>> GetAsyncStatesList()
{
//for e.g.
List<PeopleType> peopleList = new List<PeopleType>()
{
new PeopleType(){ Name = "Frank", Gender = "M" },
new PeopleType(){ Name = "Rose", Gender = "F" } //..
};
var result = from e in peopleList
where e.Gender == "M"
select e;
return await Task.FromResult(result.ToList());
}
Option 2: Use this class
public class AsyncEnumerableQuery<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T> {
public AsyncEnumerableQuery(IEnumerable<T> enumerable) : base(enumerable) {
}
public AsyncEnumerableQuery(Expression expression) : base(expression) {
}
public IDbAsyncEnumerator<T> GetAsyncEnumerator() {
return new InMemoryDbAsyncEnumerator<T>(((IEnumerable<T>) this).GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator() {
return GetAsyncEnumerator();
}
private class InMemoryDbAsyncEnumerator<T> : IDbAsyncEnumerator<T> {
private readonly IEnumerator<T> _enumerator;
public InMemoryDbAsyncEnumerator(IEnumerator<T> enumerator) {
_enumerator = enumerator;
}
public void Dispose() {
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken) {
return Task.FromResult(_enumerator.MoveNext());
}
public T Current => _enumerator.Current;
object IDbAsyncEnumerator.Current => Current;
}
}
// FindAll: with a condition like .Find(x => x.user == "Jone Doe")
// see [here][3]
var task = collection.Find(p => true).ToListAsync();
Update Option 3 : Simple Async Get
public async Task<IEnumerable<MyMongoEntity>> Where(Expression<Func<MyMongoEntity, bool>> expression = null)
{
return await context.GetCollection<MyMongoEntity>(typeof(MyMongoEntity).Name, expression).Result.ToListAsync();
}
Based on your comment, for a simple get documents collection, this helper should work.
From your error, it seems like the mongo_context.collectionName
is returning something from Entity Framework?
Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
Make sure you are calling the AsQueryable
extension method directly on the Mongo collection . (Your code just shows mongo_context.collectionName.AsQueryable()
so I'm not sure you're doing that)
Hooking into the LINQ provider requires getting access to an
IQueryable
instance. The driver provides anAsQueryable
extension method onIMongoCollection
.var collection = db.GetCollection<Person>("people"); var queryable = collection.AsQueryable();
Reference: https://mongodb.github.io/mongo-csharp-driver/2.10/reference/driver/crud/linq/#queryable
The AsQueryable
extension above actually returns an IQueryable
instance that implements IMongoQueryable
and has all the same async
extensions that other ORMs (Entity Framework, NHibernate, etc.) have - including ToListAsync
.
I just changed the way I query my context usind Find and and ForEachAsync and now all works well I just did not use AsQueryable because the mongodb driver as I understand it use these other functions but provides a way to use linq so I used the default methods without linq
var result = new GetPendingProcessesViewModel();
var filter = Builders<Domain.MongoDocuments.Process>.Filter
.Where(x => own_processes.Contains(x.Id));
await _elba_mongo_context.process.Find(filter)
.ForEachAsync(x=>result.pending_processes_own.Add(
new GetPendingProcessesViewModelItem()
{
process_id = x.Id,
state = new States()
{
timeline = x.states.timeline,
part = x.states.part,
supplier = x.states.supplier
}}));
this is the link where you can get the documentation and references for your mongodb driver version https://mongodb.github.io/mongo-csharp-driver/
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.