EF не генерирует правильный SQL SELECT WHERE при использовании динамического скомпилированного предиката

#c# #.net #entity-framework #linq-to-entities #where-clause

#c# #.net #entity-framework #linq-to-entities #where-предложение

Вопрос:

Я пытаюсь создать динамический скомпилированный предикат и использовать его для запроса Entity Framework ObjectSet. Выполняется следующий код и возвращается правильный результат, но оператор SQL SELECT генерируется без предложения WHERE:

  var db = new DBHelper();

 ParameterExpression entity = Expression.Parameter(typeof(HighSchoolServicesDataAccess.Faculty), "entity");
 var filterentity = Expression.Lambda(Expression.Equal(Expression.Property(entity, "HighSchoolID"), Expression.Constant(90, typeof(int))), entity);
 Func<HighSchoolServicesDataAccess.Faculty, bool> predicate = (Func<HighSchoolServicesDataAccess.Faculty, bool>)filterentity.Compile();

 var res = db.DBContext.Faculties.Where(predicate);
 dataGridView2.DataSource = res.ToList();
  

Сгенерированный оператор SQL является:

 SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[HighSchoolID] AS [HighSchoolID], 
    [Extent1].[TypeID] AS [TypeID], 
    [Extent1].[Name] AS [Name]
FROM [dbo].[Faculties] AS [Extent1]
  

и когда я использую явное выражение типа

 var res = db.DBContext.Faculties.Where(f => f.HighSchoolID == 90);
  

генерируется правильный SQL.

 SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[HighSchoolID] AS [HighSchoolID], 
    [Extent1].[TypeID] AS [TypeID], 
    [Extent1].[Name] AS [Name]
FROM [dbo].[Faculties] AS [Extent1]
WHERE 90 = [Extent1].[HighSchoolID]
  

Как я могу заставить EF создать предложение WHERE в SQL?

Ответ №1:

Не уверен, но может быть, вы устанавливаете predicate значение compiled Func<...> и не сохраняете его как Expression<Func<...>> , поэтому он преобразуется в IL, прежде чем EF сможет его обработать? Вы пробовали, например

 var predicate = 
   (Expression<Func<HighSchoolServicesDataAccess.Faculty, bool>>)filterentity;
  

?

Ответ №2:

Вы создали предикат, но в конце вы его скомпилировали, что означает, что он больше не является выражением, и как только вы передаете его в Where, вы используете linq-to-objects вместо linq-to-entities. Это означает, что все записи передаются в приложение, а фильтрация выполняется в памяти.

Попробуйте это вместо:

 var res=db.DBContext.Faculties.Where(filterentity);
dataGridView2.DataSource = res.ToList();
  

Комментарии:

1. Я не согласен, плохо загружать все и применять фильтрацию в памяти, это все равно будет делать то же самое. Это не приведет к созданию другого SQL, он все равно будет генерировать тот же SQL.

Ответ №3:

Linq требуется выражение функции, а не скомпилированная функция. Пожалуйста, используйте reflector, чтобы увидеть, что генерируется компилятором для вашего выражения linq.

Дерево выражений Enire должно быть доступно для предложения Linq where без компиляции.

  var db = new DBHelper();

 ParameterExpression entity = 
    Expression.Parameter(typeof(HighSchoolServicesDataAccess.Faculty), 
        "entity");

 Expression<Func<HighSchoolServicesDataAccess.Faculty, bool>> filterentity = 
    Expression.Lambda<Func<HighSchoolServicesDataAccess.Faculty, bool>>(
    Expression.Equal(
      Expression.Property(entity, "HighSchoolID"), 
        Expression.Constant(90, typeof(int))), entity);

 // not at all needed...
 //Func<HighSchoolServicesDataAccess.Faculty, bool> predicate =
 //   (Func<HighSchoolServicesDataAccess.Faculty, bool>)filterentity.Compile();

 var res = db.DBContext.Faculties.Where(filterentity);
 dataGridView2.DataSource = res.ToList();