3

I was trying to retrieving data from a collection as follow

var results = await repository.FindAsync(GetFilter(ids));

private System.Linq.Expressions.Expression<Func<SomeEntity, bool>> GetFilter(IEnumerable<long> someIds)
{
    return x => someIds.Contains(x.Id) &&
                x.subscriptions
               .Where(s => s.servicePlans.Contains((int)ServicePlanEnum.Service1)).Any();
}

but I got the following exception

System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.ConstantExpression' to type 'System.Linq.Expressions.LambdaExpression'.

Where am I wrong?

EDIT:

Subscription object looks like:

subscription :
{
    prop1 : string,
    prop2 : int,
    servicePlans : int[]
}

FindAsync definition:

protected IMongoCollection<TEntity> Collection => database.GetCollection<TEntity>(collectionName);
private IFindFluent<TEntity, TEntity> Find(Expression<Func<TEntity, bool>> predicate) => Collection.Find(predicate);
public virtual async Task<ICollection<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, SortOrder<TEntity>[] sort = null, int? page = null, int? limit = null)
{
    var query = Find(predicate);

    return await
        query
            .Skip(page == null && limit == null ? null : (page - 1) * limit)
            .Limit(limit)
            .Sort(sort)
            .ToListAsync();
}

The error is caused by the following condition

x.subscriptions.Where(s => s.servicePlans.Contains((int)ServicePlanEnum.Service1)).Any();

EDIT:

stacktrace:

Unable to cast object of type 'System.Linq.Expressions.ConstantExpression' to type 'System.Linq.Expressions.LambdaExpression'. System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.ConstantExpression' to type 'System.Linq.Expressions.LambdaExpression'. at MongoDB.Driver.Linq.ExpressionHelper.GetLambda(Expression node) at MongoDB.Driver.Linq.Processors.EmbeddedPipeline.MethodCallBinders.AnyBinder.Bind(PipelineExpression pipeline, EmbeddedPipelineBindingContext bindingContext, MethodCallExpression node, IEnumerable1 arguments) at MongoDB.Driver.Linq.Processors.MethodInfoMethodCallBinder1.Bind(PipelineExpression pipeline, TBindingContext bindingContext, MethodCallExpression node, IEnumerable1 arguments) at MongoDB.Driver.Linq.Processors.CompositeMethodCallBinder1.Bind(PipelineExpression pipeline, TBindingContext bindingContext, MethodCallExpression node, IEnumerable1 arguments) at MongoDB.Driver.Linq.Processors.PipelineBinderBase1.BindMethodCall(MethodCallExpression node) at MongoDB.Driver.Linq.Processors.EmbeddedPipeline.EmbeddedPipelineBinder.Bind(Expression node, IBindingContext parent) at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitMethodCall(MethodCallExpression node) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node) at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitBinary(BinaryExpression node) at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor) at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node) at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate[TDocument](Expression1 predicate, IBsonSerializer1 parameterSerializer, IBsonSerializerRegistry serializerRegistry) at MongoDB.Driver.MongoCollectionImpl1.CreateFindOperation[TProjection](FilterDefinition1 filter, FindOptions2 options) at MongoDB.Driver.MongoCollectionImpl1.FindAsync[TProjection](IClientSessionHandle session, FilterDefinition1 filter, FindOptions2 options, CancellationToken cancellationToken) at MongoDB.Driver.MongoCollectionImpl1.<>c__DisplayClass43_01.b__0(IClientSessionHandle session) at MongoDB.Driver.MongoCollectionImpl1.UsingImplicitSessionAsync[TResult](Func2 funcAsync, CancellationToken cancellationToken) at MongoDB.Driver.IAsyncCursorSourceExtensions.ToListAsync[TDocument](IAsyncCursorSource1 source, CancellationToken cancellationToken) at ...Repository.MongoRepository2.FindAsync(Expression1 predicate, SortOrder1[] sort, Nullable1 page, Nullable1 limit) in ...\Infastructure\Repositories\MongoRepository.cs:line 71 at ...Controllers.AccountController.GetCliniciansByClinicAsync(String clinicId, CancellationToken cancellationToken) in ...\Controllers\AccountController.cs:line 94 at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()

12
  • Don't do the cast in the expression store this in a variable (int)SomeEnum.Member Commented Oct 19, 2018 at 19:32
  • What is the argument type in FindAsync()? Commented Oct 19, 2018 at 19:48
  • @johnny5 I didn't understand what you said, (int)SomeEnum.Member is a constant (enum). Commented Oct 19, 2018 at 20:06
  • 1
    I suspect that Gert Arnold is correct. Offhand I would not expect that exception from user code like what you've written. My guess would be that most likely, there is some plebbery in the query rewriter. Or, it is possible that the defect is in the expression tree generation itself, which, uh, would make it my fault. But I would start with a call stack and see exactly where things are going bad. Commented Oct 19, 2018 at 22:00
  • 1
    (If it's not 100% clear to you what "plebbery" is, a web search for "MongoDB plebbery" is a fun way to waste a little time while compiling. This was brought to my attention when I worked at Coverity.) Commented Oct 19, 2018 at 22:02

2 Answers 2

3

As johnny 5 stated Mongo's C# expression parser is fairly limited. Another thing to point out is you cannot use method groups those will yield a ConstantExpression similar to your example. The key is just leaving the full predicate expression.

Bad

var list = _collection.Find(r => r.Scopes.Any(scopeNames.Contains)).ToListAsync(cancellationToken);

Good

var list = _collection.Find(r => r.Scopes.Any(s => scopeNames.Contains(s)).ToListAsync(cancellationToken);

In the event you have ReSharper or Rider you can disable the code suggestion by putting this comment line right above your code

// ReSharper disable once ConvertClosureToMethodGroup
Sign up to request clarification or add additional context in comments.

Comments

1

I haven’t used the linq to Mongodb but it looks like you’re adding unnecessary complexities to your expression.

When you pass an expression, the code needs to be translated to a query in which mongo can handle.

Contains expects an int, in normal c# this is fine.

But as an expression an int is a constant, but you’ve provided an complex expression. e.g casting an enum to an int. Which is a convert expression.

It seems mongodb expression parser is not advanced enough to handle that. So let c# do the cast and pass it directly

var results = await repository.FindAsync(GetFilter(ids));

private System.Linq.Expressions.Expression<Func<SomeEntity, bool>> GetFilter(IEnumerable<long> someIds)
{
    var serviceVal = (int)ServicePlanEnum.Service1;
    return x => someIds.Contains(x.Id) &&
            x.subscriptions
           .Where(s => s.servicePlans.Contains( serviceVal ).Any();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.