[CSHARP-4521] Handle parameterName empty string the same as null Created: 09/Feb/23  Updated: 28/Oct/23  Resolved: 14/Feb/23

Status: Closed
Project: C# Driver
Component/s: LINQ3
Affects Version/s: 2.19.0
Fix Version/s: 2.19.1

Type: Bug Priority: Major - P3
Reporter: Andy Bennett Assignee: Robert Stam
Resolution: Fixed Votes: 0
Labels: Bug
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Documentation Changes: Not Needed
Documentation Changes Summary:

1. What would you like to communicate to the user about this feature?
2. Would you like the user to see examples of the syntax and/or executable code and its output?
3. Which versions of the driver/connector does this apply to?


 Description   

Summary

MongoQueryProvider broken in v2.19, throwing the following exception:

 

2023-02-09 10:49:08.345 +00:00 [ERR] Value cannot be empty. (Parameter 'name')
System.ArgumentException: Value cannot be empty. (Parameter 'name')
   at MongoDB.Driver.Core.Misc.Ensure.IsNotNullOrEmpty(String value, String paramName)
   at MongoDB.Driver.Linq.Linq3Implementation.Misc.Symbol..ctor(ParameterExpression parameter, String name, AstExpression ast, IBsonSerializer serializer, Boolean isCurrent)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.TranslationContext.CreateSymbol(ParameterExpression parameter, String name, AstExpression ast, IBsonSerializer serializer, Boolean isCurrent)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.TranslationContext.CreateSymbol(ParameterExpression parameter, String name, String varName, IBsonSerializer serializer, Boolean isCurrent)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.TranslationContext.CreateSymbolWithVarName(ParameterExpression parameter, String varName, IBsonSerializer serializer, Boolean isCurrent)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ExpressionToFilterTranslator.TranslateLambda(TranslationContext context, LambdaExpression lambdaExpression, IBsonSerializer parameterSerializer, Boolean asRoot)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToPipelineTranslators.WhereMethodToPipelineTranslator.Translate(TranslationContext context, MethodCallExpression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToPipelineTranslators.ExpressionToPipelineTranslator.Translate(TranslationContext context, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators.CountMethodToExecutableQueryTranslator.Translate[TDocument](MongoQueryProvider`1 provider, TranslationContext context, MethodCallExpression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToExecutableQueryTranslators.ExpressionToExecutableQueryTranslator.TranslateScalar[TDocument,TResult](MongoQueryProvider`1 provider, Expression expression)
   at MongoDB.Driver.Linq.Linq3Implementation.MongoQueryProvider`1.Execute[TResult](Expression expression)
   at ScafellServer.DataAccess.Extensions.LinqExtensions.OptionalSkip[TSource](IQueryable`1 source, Nullable`1 count) in C:\Users\AndyBennett\source\repos\Scafell\src\ScafellServer\DataAccess\Extensions\LinqExtensions.cs:line 19

 

 

NB. versions v2.18 and before work as expected.

Code:

public static IQueryable<TSource> OptionalSkip<TSource>(this IQueryable<TSource> source, int? count)
{
    try
    {
        return count.HasValue && source?.Count() >= count.Value
            ? source.Skip(count.Value)
            : source;
    }
    catch (Exception ex)
    {
        Logger.Error(ex, "{Message}", ex.Message);
        return source;
    }
} 



 Comments   
Comment by Githook User [ 24/Mar/23 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-4521: Handle parameterName empty string the same as null.
Branch: v2.19.x
https://github.com/mongodb/mongo-csharp-driver/commit/0b1f14a62c72d009d3f7d975c639fdef3f14742d

Comment by Githook User [ 14/Feb/23 ]

Author:

{'name': 'rstam', 'email': 'robert@robertstam.org', 'username': 'rstam'}

Message: CSHARP-4521: Handle parameterName empty string the same as null.
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/3e62500583c1599c558411648239635b06983e97

Comment by Andy Bennett [ 10/Feb/23 ]

Many thanks, Robert.

Comment by Robert Stam [ 09/Feb/23 ]

The repro is getting kind of complicated...

Knowing that you are using dynamic LINQ and looking at the stack trace, I've determined that the problem is that dynamic LINQ is using an empty string as the parameter name.

We have code to handle a null as the parameter name, but not the empty string.

I've reproduced your issue (but not using any of your code) and have a fix for it.

Thanks again for reporting this.

Comment by Andy Bennett [ 09/Feb/23 ]

You are correct, 'filter' is a string that is dynamically constructed. I am using the 'Where' from System.Linq.Dynamic.Core which can consume a string. I did not intend it to be a C# expression.

'data' is of type IQueryable<T> data

OptionalTake is shown below.

Hope that helpd. If not, I willl attempt to create a standalone console application next week.

PS. I'm using a string rather than an expression because our UI components use a string based notation for filtering.

 

public static IQueryable<TSource> OptionalTake<TSource>(this IQueryable<TSource> source, int? count)
{
    try
    {
        return count.HasValue && source?.Count() >= count.Value
            ? source.Take(count.Value)
            : source;
    }
    catch (Exception ex)
    {
        Logger.Error(ex, "{Message}", ex.Message);
        return source;
    }
} 

Comment by Robert Stam [ 09/Feb/23 ]

Thanks for the additional information.

The `filter` looks odd. It's a `string` but it looks like you intended it to be a C# expression.

Also, where is `data` defined? Or `OptionalTake`?

It might be better if you created a standalone console application that I can compile and run and base my repro on.

Comment by Andy Bennett [ 09/Feb/23 ]

Thanks Robert. The 'Where' code is as follows:

 

string filter = "Product.ToLower().Contains("ukp.bsld.day.2022-11-02")||ProductLongName.ToLower().Contains("ukp.bsld.day.2022-11-02")";
 
return Where(filter)
    .OptionalSkip(scafellDataLoadOptions.Skip)
    .OptionalTake(scafellDataLoadOptions.Take);

 

IQueryable<T> Where(string filter)
{
    try
    {
        return data.Where(filter);
    }
    catch (Exception ex)
    {
        Logger.Error(ex, "{Message}, {Filter}", ex.Message, filter);
        return MongoCollection.AsQueryable();
    }
} 

I understand this may be data related, but the same code worked with the same data under v2.18.

Comment by Robert Stam [ 09/Feb/23 ]

I have been unable to reproduce this. You could look at my attempt here:

https://github.com/rstam/mongo-csharp-driver/tree/csharp4521

Maybe you could take a look and tell me if I missed something.

The stack trace you reported indicates that the exception was thrown while translating a `Where` method, so you probably need to show me how you were calling `Where`.

Comment by Robert Stam [ 09/Feb/23 ]

Thank you for reporting this issue. I will begin investigating it soon.

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