Как создать лямбда-выражение для Linq в предложении Where для двух таблиц после объединения? - PullRequest
0 голосов
/ 15 ноября 2018

У меня есть это,

 Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
 Func<MyCourse, bool> funcWhere = filter.Compile();

, а затем это,

var myClasses = db.MyCourse.Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).Where(???)

Причина, по которой мне нужно сделать это, заключается в том, что если я сначала добавлю фильтр в таблицу MyCourse с помощью

db.MyCourse.Where(funcWhere).Join....

Созданный SQL возвращает всех людей и весь MyCourse, а затем использует фильтр. Если я сделаю где в конце,

(mc, p) => new { MyCourse= mc, Person = p }).Where(mc=>mc.MyCourse.Active == 1)

Я получил хороший запрос от Joins. В противном случае движок сначала запрашивает все строки в памяти. Два отдельных запроса с тысячами строк.

Я видел огромное количество вопросов об SO и других местах по этому поводу. Я не могу найти тот, который говорит мне, как сделать выражение, когда существует более одной таблицы, из соединения, используя динамический Where Expression<Func<T,TResult>>.

Цель состоит в том, чтобы сделать динамический оператор запроса на основе выражений (а не Dynamic Linq и без сторонних разработчиков.) На самом деле, этот вопрос утверждает, что Где в конце медленнее, но в моей программе он делает правильный запрос с Joins.

У MyCourse есть PersonId, а у People - PersonId. Если бы я написал это от руки, это выглядело бы как

select mc.CourseName, p.LastName 
from MyCourse mc inner join Person p on mc.PersonId = p.PersonId
where mc.Active = 1;

(Это только примеры столбцов для вопроса. Они не совсем то, что я хочу от вышеупомянутого запроса, кроме Active == 1.)

Где предложение с Join в лямбда-выражении

Обновление: FWIW, я смог заставить его работать вот так,

    var param = Expression.Parameter(typeof(MyClass), "MyClassDebug");
    var exp = Expression.Lambda<Func<MyClass, bool>>(
        Expression.Equal(
            Expression.Property(param, dbParameter),
            Expression.Constant(dbValue)
        ),
        param
    );

Я не делал навигационные свойства или что-то еще. И я смог использовать это так,

var MyQuery = (from recs in dbcontext.MyClass.Where(exp)
               ...three joins

Полученный SQL выглядел хорошо, а план объяснения показал минимальное извлечение строк.

1 Ответ

0 голосов
/ 15 ноября 2018

Я подозреваю, что вызов Compile() на вашем Expression вызывает у вас проблемы. Ваш полный запрос включает Join, но вы уже скомпилировали предложение Where, поэтому он не может скомпилировать весь запрос, включая Join вместе. Возможно, именно поэтому он захватывает всю таблицу, потому что сначала он выполняет Where, а потом Join.

Но вам не нужно звонить Compile(). Просто передайте Expression в Where():

Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
var myClasses = db.MyCourse
    .Where(filter)
    .Join(db.People, mc => mc.PersonId, 
p => p.PersonId, (mc, p) => new { MyCourse= mc, Person = p }).ToList();

В некоторой степени не относится к вашей реальной проблеме, но если вы создали внешние ключи, вы можете немного упростить это. Обновите модель в проекте Visual Studio, если вы этого еще не сделали. Ваш класс Person будет иметь список MyCourse, а ваш класс MyCourse будет иметь список Person.

Так что вы можете сделать что-то вроде:

Expression<Func<MyCourse, bool>> filter = mc => mc.Active == 1;
var courses = db.MyCourse.Include("Person").Where(filter);
foreach (var course in courses) {
    var person = course.Person; //This is populated with the Person record
}

Linq обрабатывает объединение, и каждый возвращенный MyCourse будет иметь свойство Person.

...