tsubasaetkisi
tsubasaetkisi

Reputation: 311

How to build an empty generic IMongoQueryable<TEntity> type queryable object

The method I use is returning a queryable object of type IMongoQueryable<TEntity>. Result of this method returns default(IMongoQueryable<TEntity>) when mongodb database throws an error. Instead of the default result, I want to replace that row with an empty queryable object, however MongoDb Linq library does not provide any.

//Sample of the code
public IMongoQueryable<TEntity> AllQueryable<TEntity>(AggregateOptions options = null) where TEntity : Entity
{
    try
    {
        return GetCollection<TEntity>().AsQueryable(options);
    }
    catch (Exception ex)
    {
        return default(IMongoQueryable<TEntity>);
    }
}

So, how can I implement a custom type equivalent to the Enumerable.Empty<TEntity>().AsQueryable() or something like IEnumerable<TEntity>().AsMongoQueryable()?

//I need something like this
public IMongoQueryable<TEntity> AllQueryable<TEntity>(AggregateOptions options = null) where TEntity : Entity
{
    try
    {
        return GetCollection<TEntity>().AsQueryable(options);
    }
    catch (Exception ex)
    {
        return Enumerable.Empty<TEntity>().AsMongoQueryable();
    }
}

Upvotes: 2

Views: 742

Answers (1)

mickl
mickl

Reputation: 49975

When you debug your C# application you can notice that running .AsQueryable<T> on MongoDB collection returns an instance of MongoQueryableImpl class which is internal in MongoDB.Driver.Linq namespace. You can try to instantiate that class using reflection but there's an easier way.

You can implement your own implementation of IMongoQueryable<T> interface following the Null Object design pattern.

The interface requires a handful of methods but you can keep most of them unimplemented - you'll never need them. So your EmptyMongoQueryable<T> may look like this:

public class EmptyMongoQueryable<T> : IMongoQueryable<T>
{
    public Expression Expression => Expression.Constant(this, typeof(IMongoQueryable<T>));
    public Type ElementType => typeof(T);
    public IQueryProvider Provider => new EmptyQueryProvider();
    public IEnumerator<T> GetEnumerator()
    {
        yield break;
    }

    public QueryableExecutionModel GetExecutionModel() => throw new NotImplementedException();
    public IAsyncCursor<T> ToCursor(CancellationToken cancellationToken = default(CancellationToken)) => throw new NotImplementedException();
    public Task<IAsyncCursor<T>> ToCursorAsync(CancellationToken cancellationToken = default(CancellationToken)) => throw new NotImplementedException();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

Then you can run following code:

var queryable = new EmptyMongoQueryable<YourType>();
var emptyList = queryable.ToList();

EDIT: since you've mentioned in a comment that you want to run .Where() you need to provide the IQueryProvider. Same story - MongoDB driver uses internal class but you can create your own:

public class EmptyQueryProvider : IQueryProvider
{
    public IQueryable CreateQuery(Expression expression) => null;
    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) => new EmptyMongoQueryable<TElement>();
    public object Execute(Expression expression) => default(object);
    public TResult Execute<TResult>(Expression expression) => default(TResult);
}

This will allow you to run Where() on your EmptyMongoQueryable<T>.

Upvotes: 1

Related Questions