[CSHARP-3702] Expression.Not not supported for null value in expression parameter Created: 03/Jun/21  Updated: 27/Oct/23  Resolved: 08/Jun/21

Status: Closed
Project: C# Driver
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Unknown
Reporter: Антон Сухоруков Assignee: Boris Dogadov
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

We have expression for find recodrs:

 

Expression<Func<Customer, bool>> SourceExpression => customer => customer.PhoneNumber == null; 

Also we have function for negation expressions

 

 

Expression<Func<T, bool>> NegationExpression =>
    System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(
        System.Linq.Expressions.Expression.Not(SourceExpression.Body), SourceExpression.Parameters)

 

In method Find after applying  NegationExpression for  Source Expression driver throw next exception:

 

System.InvalidOperationException: x.PhoneNumber is not supported.System.InvalidOperationException
x.PhoneNumber is not supported.
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.GetFieldExpression(Expression expression)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateComparison(BinaryExpression binaryExpression)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.TranslateNot(UnaryExpression unaryExpression)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate(Expression node, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate[TDocument](Expression`1 predicate, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.ExpressionFilterDefinition`1.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.MongoCollectionImpl`1.CreateFindOperation[TProjection](FilterDefinition`1 filter, FindOptions`2 options)
   at MongoDB.Driver.MongoCollectionImpl`1.FindAsync[TProjection](IClientSessionHandle session, FilterDefinition`1 filter, FindOptions`2 options, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.<>c__DisplayClass47_0`1.<FindAsync>b__0(IClientSessionHandle session)
   at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSessionAsync[TResult](Func`2 funcAsync, CancellationToken cancellationToken)
   at MongoDB.Driver.IAsyncCursorSourceExtensions.ToListAsync[TDocument](IAsyncCursorSource`1 source, CancellationToken cancellationToken)

 

 



 Comments   
Comment by Антон Сухоруков [ 08/Jun/21 ]

Your advice helped, and the problem is no longer reproduced! thanks =)

Comment by Boris Dogadov [ 07/Jun/21 ]

Hi antonsuhorukov94@gmail.com,
Thank you for providing the full app.

In the provided example, CustomerNotNullSpecification.Expression returns new expression instance on each invocation. As NotSpecification.Expression constructs Not expression using two different Expression instances, the internal parameters biding fails. Changing NotSpecification.Expression to 

public override Expression<Func<T, bool>> Expression
{
    get
    {
        var expression = _spec.Expression;
        return System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(
            System.Linq.Expressions.Expression.Not(expression.Body), expression.Parameters);
    }
}

resolves the problem in the attached application.

Please let us know whether this fix resolves the issue in your application.
Thanks!

 

Comment by Антон Сухоруков [ 07/Jun/21 ]

Version of driver is 2.11.6

self-contained app:

public static void Main()
{
    var client = new MongoClient();
    var db = client.GetDatabase("test");
    var coll = db.GetCollection<Customer>("coll");
 
    var specification = new CustomerNotNullSpecification();
    var negationExpression = new NotSpecification<Customer>(specification);
    
    var query = coll.Find(negationExpression.Expression).ToList();
    Console.WriteLine(query.ToString());
}
 
public abstract class Specification<T> where T : class
{
    public abstract Expression<Func<T, bool>> Expression { get; }
}
 
public class CustomerNotNullSpecification : Specification<Customer>
{
    public override Expression<Func<Customer, bool>> Expression => x => x.PhoneNumber == null;
}
 
public class NotSpecification<T> : Specification<T> where T : class
{
    private readonly Specification<T> _spec;
 
    public NotSpecification(Specification<T> spec)
    {
        _spec = spec;
    }
 
    public override Expression<Func<T, bool>> Expression =>
        System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(
            System.Linq.Expressions.Expression.Not(_spec.Expression.Body), _spec.Expression.Parameters);
}
 
public class Customer
{
    public string PhoneNumber { get; set; }
}

Comment by James Kovacs [ 04/Jun/21 ]

Hi, antonsuhorukov94@gmail.com,

We attempted to reproduce the reported issue with the following code, but it did not result in the InvalidOperationException that you encountered:

using System;
using System.Linq.Expressions;
using MongoDB.Driver;
 
public class Program
{
    public static void Main()
    {
        var client = new MongoClient();
        var db = client.GetDatabase("test");
        var coll = db.GetCollection<Customer>("coll");
 
        Expression<Func<Customer, bool>> sourceExpression = customer => customer.PhoneNumber == null;
        Expression<Func<Customer, bool>> negationExpression = Expression.Lambda<Func<Customer, bool>>(Expression.Not(sourceExpression.Body), sourceExpression.Parameters);
 
        var filter = Builders<Customer>.Filter.Where(negationExpression);
        var query = coll.Find(filter);
        Console.WriteLine(query.ToString());
    }
 
    public class Customer
    {
        public string PhoneNumber { get; set; }
    }
}

Output:

find({ "PhoneNumber" : { "$ne" : null } })

In order to investigate further, please provide:

  • version of MongoDB .NET/C# driver
  • self-contained repro demonstrating the problem

We look forward to working with you to reproduce and resolve this issue.

Sincerely,
James

Comment by Esha Bhargava [ 04/Jun/21 ]

antonsuhorukov94@gmail.com Thank you for reporting this issue! We'll look into it and get back to you soon.

Generated at Wed Feb 07 21:46:03 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.