Search code examples
c#.netlinqentity-framework-6expression-trees

Linq: let statement as a constant?


I am working on a Linq query expression statement, and want to use a let statement to set a constant value as part of the query to reduce potential errors.

In my example... (this is total mock up). the let statement "validateCount" is the question.

    from referenceRow in context.SomeTable
    // We want 8 in the Take and the results to still be 8 after the where. 
    // instead of typing in the value twice (in this example), i wanted to use a constant value here
    let validateCount = 8

    // sub query expression in my example
    let subQuery = from sub in context.SomeTable
               where sub.SomeColumn == rowReference.SomeColumn
               orderby sub.SomeColumn descending
               select sub

// use the sub query, take X amount, apply filter, check to see if Count() is the same as Take()
where subQuery.Take(validateCount) // this is where we fail. if we put in the constant 8, we are ok.
              .Where(x => x.SomeOtherColumn == "Some value")
              .Count() == validateCount // this is fine
select referenceRow

Unfortunately it seems that the let expression "validateCount" which has a single value of 8 in this example, can only work in the comparison part of .Count() but cannot be passed into the .Take() without throwing an exception.

Limit must be a DbConstantExpression or a DbParameterReferenceExpression. Parameter name: count

Looking for a solution to use some user-defined constant in a single code location that can be used in the rest of the query expression, both in the .Take() and .Count() without having to be updated several spots in the code.

our application allows users to supply their own linq query expression to build their queries. I can't define anything outside the scope of this query, and must be within, using something like a 'let' statement.


Solution

  • let statement generates intermediate anonymous type projection (Select call) in the query expression tree. EF query provider (as indicated by the exception message) requires Skip and Take arguments to be resolved to constant or variable values (i.e. to be able to be evaluated locally), hence the let cannot be used for that purpose.

    Instead, the constants/variables used in Skip / Take expressions should be defined outside of the query and used inside.

    To define a constant value you would use:

    const int validateCount = 8;
    var query = (from .... Take(validateCount) ...);
    

    To define a variable value (SQL query parameter):

    int validateCount = 8;
    var query = (from .... Take(validateCount) ...);
    

    Here the C# compiler will turn validateCount into closure and EF query provider will be happy to bind a parameter (with that value).

    our application allows users to supply their own linq query expression to build their queries. I can't define anything outside the scope of this query, and must be within, using something like a 'let' statement.

    When supplying their own queries, the users should follow the same Skip / Take argument rules as above, i.e. define their constants and variables outside of their queries.