Invalidoperationexception: the LINQ expression

sblb 1,231 Reputation points
2021-10-22T15:46:31.333+00:00

Hi,
I have the message

System.InvalidOperationException: The LINQ expression 'DbSet<Developer>()
    .Where(d => d.Calcul < 0)' could not be translated. Additional information: Translation of member 'Calcul' on entity type 'Developer' failed. This commonly occurs when the specified member is unmapped. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|15_0(ShapedQueryExpression translated, <>c__DisplayClass15_0& )
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.Count[TSource](IQueryable`1 source)

In my controller I've definie linq Expression:

public IActionResult MonthlyStats()
        {

            double NombreECR = context.Developers
                         .Where(number => number.Calcul < 0)
                         .Count();

            return Ok(NombreECR);
}

In Developer.cs I've defined Developer.Calcul

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public int Calcul
        {
            get
            {
                if (DateSoldePr.Year < 2000)
                {
                    return 0;
                }
                if (DateSoldePr.Year > 2000)
                {
                    return (int)Math.Floor((DateTime.Today - DateSoldePr).TotalDays);
                }
                else
                {
                    return 0;
                }
            }
        }

do you have any idea how I can solve the problem? I am really stuck in my project
Thanks in advance

Developer technologies | .NET | Blazor
Developer technologies | C#
0 comments No comments
{count} votes

Accepted answer
  1. Viorel 122.6K Reputation points
    2021-10-22T16:07:07.377+00:00

    Try this:

    double NombreECR = context.Developers.ToList( ).Count( number => number.Calcul < 0);
    

    or something like this:

    double NombreECR = context.Developers.Count( number => number.DateSoldePr > DateTime.Today);
    
    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Michael Taylor 60,331 Reputation points
    2021-10-22T15:56:54.68+00:00

    You are misusing the property and EF cannot translate that. In order to use an expression in LINQ it must be translatable to the underlying provider, EF in your case. For that to happen the property must be get/settable. In your case you're trying to use a calculated property. That is not accessible inside the DB and therefore cannot be used in the LINQ expression that EF has to generate.

    If you think about it a second it makes sense. How exactly would EF translate your number.Calcui > 0 to SQL? Ultimately this has to result in a SQL clause that looks like this sometable.Calcui > 0 so the sometable table would need a Calcui column. In your case there isn't one so there is no way for the SQL statement to be generated. Calculated properties (nor unmapped properties) cannot be used in LINQ statements backed by EF.

    You have a couple of options to fix this. If this is truly a database column then change it from a calculated property and make it settable. EF can then retrieve the value when it makes the DB call. If this is a calculated property then it cannot be used in the LINQ expression. You can remove the DatabaseGenerated attribute because it means nothing on a calculated property. You don't even need to mark it as unmapped because EF doesn't map properties that don't have a setter (unless you're using field setters but that it is a different discussion).

    The only way to use a calculated/unmapped property in EF is to break up the query into 2 pieces. The first piece can run in the DB and is going to be efficient. Then you force the execution of the query (generally a Select or To... call). You have now pulled back the data from the DB. You can then use Where to filter the (now in memory) resultset using any .NET expression.

    //Query to run on the DB
    var results = context.MyStuff.Where(x => x.SomeDbColumn > 0);
    
    //Force execution
    results = results.Select(x => x);
    
    //Now running on the in-memory version so any expression is allowed
    results = results.Where(x => x.Calcui > 0);
    

    Just be warned that the DB query needs to filter as much data as possible otherwise you could end up pulling back all your data and then applying a filter on the .NET side which would be expensive.

    A final option for your specific case would be to create a view that does the column calculation in SQL. Then update your query to use the view instead of the table. You can then use a get/set property on the model and the Where expression can be run in the DB again.

    1 person found this answer helpful.

  2. sblb 1,231 Reputation points
    2021-10-23T09:19:15.55+00:00

    Thank you for your answers.
    I understand now that calculated columns cannot be used for LINQ expressions but I can use them to display values in my interface.

    I have both methods you suggest with the .Calcul column and it still doesn't work.
    I don't know why I didn't think of this but the solution below works (great!).

     double NombreECR = context.Developers.Count( number => number.DateSoldePr > DateTime.Today);
    

    I would like to add a ratio calculation to my LINQ expression as follows, now I have a format problem
    Is the LINQ expression correct?

        public IActionResult MonthlyStats()
            {
    
                  int numberecr = context.Developers.Count();
                  int calculN = context.Developers.Count(number => number.DateSoldePr > DateTime.Today);
    
                  int data = calculN / numberecr;
    
                  var stats = context.Developers
                               .Select(group => new 
                                 {
                                      NombreECR = numberecr,
                                      Ratio = data
                                 });
                   return Ok(stats);
             }
    

    I pass the values in the following way

       public class Stats
             {
                public int NombreECR { get; set; }
                public int Ratio { get; set; }
                public int stats { get; set; }
    
            }
    
             public async Task<Stats> MonthlyStats()
               {
                var response = await Http.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri($"{UriHelper.BaseUri}api/servermethods/MonthlyStats")));
                 string jsonStr = await response.Content.ReadAsStringAsync();
                  Stats re = new Stats { stats = int.Parse(jsonStr)};
                  return re;
               // return await response.ReadAsync<Stats>();
    
            }
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.