Используйте опциональное предложение OR в Linq.Table.Where ()

2

Есть ли способ сделать проверку ProjectID ниже части дополнительного блока? Я недавний конвертер .Net из Java EE, и я ищу что-то похожее на API-интерфейс Hibernate. Я хотел бы упростить блок ниже и только нужно вызвать Where() один раз. Я также не уверен в производительности, связанной с выполнением функции Where() с lambdas, поскольку я только начал работать с .Net неделю назад.

public IQueryable<Project> FindByIdOrDescription(string search)
    {
        int projectID;
        bool isID = int.TryParse(search, out projectID);

        IQueryable<Project> projects;

        if (isID)
        {
            projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search) || p.ProjectID == projectID);
        }
        else
        {
            projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search));
        }

        return projects;
    }
Теги:
lambda
linq-to-sql

3 ответа

3
Лучший ответ

Если вы ищете необязательное добавление AND xyz, вы можете просто вызвать вызовы Where:

var projects = dataContext.Projects.Where(p => p.ProjectDescription.Contains(search));
if (isID)
    projects = projects.Where(p => p.ProjectID == projectID);

К сожалению, все не так просто, когда вы хотите сделать OR xyz. Чтобы это сработало, вам нужно будет создать предикатное выражение вручную. Это не очень. Один из способов сделать это -

Expression<Func<Project, bool>> predicate = p => p.ProjectDescription.Contains(search);
if (isID)
{
    ParameterExpression param = expr.Body.Parameters[0];
    predicate = Expression.Lambda<Func<Project, bool>>(
        Expression.Or(
            expr.Body,
            Expression.Equal(
                Expression.Property(param, "ProjectID"),
                Expression.Constant(projectID))),
        param);
}
var projects = dataContext.Projects.Where(predicate);

Обратите внимание, что добавление условия к существующему предикату намного больше, чем создание исходного выражения, потому что нам нужно полностью перестроить выражение. (Предикат должен всегда использовать один параметр, объявив два выражения, используя синтаксис лямбда, создаст два отдельных объекта выражения параметров, по одному для каждого предиката.) Обратите внимание, что компилятор С# делает примерно то же самое за кулисами, когда вы используете синтаксис лямбда для начального предикат.

Обратите внимание, что это может показаться знакомым, если вы привыкли к API критериев для Hibernate, немного более подробным.


Обратите внимание, однако, что некоторые реализации LINQ довольно умны, поэтому может также работать следующее:

var projects = dataContext.Projects.Where(
    p => p.ProjectDescription.Contains(search)
      || (isID && p.ProjectID == projectID));

YMMV, тем не менее, нужно проверить сгенерированный SQL.

  • 0
    Да, это выглядит существенно сложнее, чем создание нового Критерия в Hibernate. Я предполагаю, что LINQ не поддерживает ничего подобного.
  • 0
    Правда, это очень многословно. Тем не менее, может быть ярлык для вашей ситуации. Я расширил описание, чтобы упомянуть об этом.
1

Запрос не анализируется и не выполняется до тех пор, пока вы в первый раз не получите доступ к элементам IQueryable. Поэтому, независимо от того, сколько мест вы добавляете (что вы даже не делаете здесь в любом случае), вам не нужно беспокоиться о том, чтобы слишком часто ударять БД.

0

Делегаты/выражения могут быть "закованы в цепочку", как этот (непроверенный псевдокод):

Expression<Predicate<Project>> predicate = p => p.ProjectDescription.Contains(search);
if ( isID ) 
    predicate = p => predicate(p) || p.ProjectID == projectId;

return dataContext.Where(predicate);
  • 0
    К сожалению, это даже не скомпилируется. Выражения нельзя вызывать внутри выражений, подобных этому.
  • 0
    Есть ли другой способ сделать это в LINQ? Возможно, без лямбд?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню