简体   繁体   中英

Getting mongodb documents but ToListAsync on Iqueriable throws exception The source IQueryable doesn't implement IAsyncEnumerable

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 an AsQueryable extension method on IMongoCollection .

 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM