LINQ - Динамическое построение запроса
От: J_K  
Дата: 16.07.08 21:09
Оценка:
Здравствуйте,
помогите разобраться. У меня на сайте пользователь может выбрать некоторые критерии, в соответствии с которыми производится выборка. Поскольку критериев много, решено было отказаться от ХП и использовать только LINQ. Но тут возник затык — оказалось, я не знаю, как же сделать запрос динамическим. Предположим, надо выбрать покупателей, относящихся к определенным категориям или категории. Т.е. sql запросы могут быть такими:

select * from customers where category = 4;

select * from customers where category = 4 and category = 8;

select * from customers where category = 4 and category = 8 and category = 9;

select * from customers;


В последнем случае пользователю надо выбрать всех покупателей безотносительно категории.

Предполагалось, что запрос на LINQ мог бы выглядеть примерно следующим образом (это ПСЕВДОКОД!)
Категории, для которых надо производить выборку, содержатся в List<int>


List<int> categories = new List<int>(){4,8}; //на самом деле массив передается в качестве параметра и содержит произвольный набор категорий и может быть пустым

//дальше идет псевдокод

var result;
foreach(int cat in categories)
{
    result = customers.Where(r => r == cat);
}
result = result.Select();


Т.е. суть в том, чтобы динамически производить операцию where в зависимости от выбора пользователя.
Данный код, естественно, не работает.
Как заставить его работать? Может, вообще как-то по-другому это делается?
Спасибо
Life is very short and there's no time
for fussing and fighting... (C) Paul McCartney & John Lennon
Re: LINQ - Динамическое построение запроса
От: TK Лес кывт.рф
Дата: 16.07.08 21:40
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>Как заставить его работать? Может, вообще как-то по-другому это делается?


Поставьте себя на место LINQ — как можно из вашего foreach сгенерировать хотя бы минимально эффективный SQL запрос?
Попробуйте сделать Join для таблиц customers и categories
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re: LINQ - Динамическое построение запроса
От: Qbit86 Кипр
Дата: 16.07.08 22:30
Оценка: +2
Здравствуйте, J_K, Вы писали:

J_K>
select * from customers where category = 4 and category = 8;

select * from customers where category = 4 and category = 8 and category = 9;


Если честно, я в БД, СУБД и SQL плохо разбираюсь. Но что-то мне подсказывает, что эти два запроса всегда возвращают пустое множество. Возможно, это «что-то» ошибается.

J_K>//дальше идет псевдокод...


Что-то я не смекнул, каким образом этот псевдокод иллюстрирует проблему... Ну, неважно.

J_K>Т.е. суть в том, чтобы динамически производить операцию where в зависимости от выбора пользователя.


Исходя из того, как я понял проблему (а быть может, я её понял неправильно), могу предложить два пути решения.
1) Если у пользователя есть лишь небольшое число вариантов выбора, то можно просто в switch'e или из таблицы выбрать соответствующую функцию или передать непосредственно лямбду.
2) Если выбор пользователя может быть произвольным, то можно сконструировать прямо в рантайме соответствующее expression tree, а затем передать его в запрос. Для каждой LINQ-функции, принимающей лямбду, вроде есть аналогичная функция, принимающая expression tree. Так что этот путь по сути предполагает построение интерпретатора выражений. Возможно, будет проще сгенерировать интерпретатор программой типа GOLD.
Глаза у меня добрые, но рубашка — смирительная!
Re: LINQ - Динамическое построение запроса
От: adontz Грузия http://adontz.wordpress.com/
Дата: 17.07.08 00:38
Оценка: -3
Здравствуйте, J_K, Вы писали:

J_K>помогите разобраться. У меня на сайте пользователь может выбрать некоторые критерии, в соответствии с которыми производится выборка. Поскольку критериев много, решено было отказаться от ХП и использовать только LINQ.


Linq тут вряд ли поможет, я бы сгенерировал все нужные варианты ХП автоматом, назвал бы и по каким-то правилом и при выборке генерировал бы имя ХП по тем же правилам, чтобы найти нужную.
Связность, конечно, высокая, но альтернативой является, пожалуй, только навороченная O/R M
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: LINQ - Динамическое построение запроса
От: Кэр  
Дата: 17.07.08 03:06
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>
J_K>select * from customers where category = 4;

J_K>select * from customers where category = 4 and category = 8;

J_K>select * from customers where category = 4 and category = 8 and category = 9;

J_K>select * from customers;
J_K>


Может быть там имеется ввиду or, а не and? Может тогда in поможет?

J_K>

J_K>List<int> categories = new List<int>(){4,8}; //на самом деле массив передается в качестве параметра и содержит произвольный набор категорий и может быть пустым

J_K>//дальше идет псевдокод

J_K>var result;
J_K>foreach(int cat in categories)
J_K>{
J_K>    result = customers.Where(r => r == cat);
J_K>}
J_K>result = result.Select();
J_K>


Здесь в цикле result переприсвается. Смысла в этом не очень много.
Re: LINQ - Динамическое построение запроса
От: Аноним  
Дата: 17.07.08 03:33
Оценка: 14 (4) -2
Здравствуйте, J_K, Вы писали:

J_K> я не знаю, как же сделать запрос динамическим.

посмотрите Dynamic Linq
Re: LINQ - Динамическое построение запроса
От: Ziaw Россия  
Дата: 17.07.08 06:23
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>
J_K>select * from customers where category = 4;

J_K>select * from customers where category = 4 and category = 8;

J_K>select * from customers where category = 4 and category = 8 and category = 9;

J_K>select * from customers;
J_K>


Вы перепутали or с and.

J_K>

J_K>List<int> categories = new List<int>(){4,8}; //на самом деле массив передается в качестве параметра и содержит произвольный набор категорий и может быть пустым

J_K>//дальше идет псевдокод

J_K>var result = customers;
J_K>foreach(int cat in categories)
J_K>{
J_K>    result = result.Where(r => r == cat);
J_K>}
J_K>//result = result.Select();
J_K>


J_K>Т.е. суть в том, чтобы динамически производить операцию where в зависимости от выбора пользователя.

J_K>Данный код, естественно, не работает.
J_K>Как заставить его работать? Может, вообще как-то по-другому это делается?
J_K>Спасибо
опять же вы перепутали условие, правильно будет:
result = customers.Where(c => categories.Contains(c.category));
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re: LINQ - Динамическое построение запроса
От: RushDevion Россия  
Дата: 17.07.08 06:26
Оценка:
Если выбор нужен только по категориям, то как-то так:
List<int> targetCategories = new List<int>();
// Заполняем
var p = from с in Cusromers where targetCategories.Contains(с.category);

Если что-то более сложное, то либо Dynamic linq, либо хранимки.
Re: LINQ - Динамическое построение запроса
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.07.08 07:18
Оценка: 7 (2) +2
Здравствуйте, J_K, Вы писали:

J_K>помогите разобраться.

Вижу несколько ошибок и проблем.

1. Во-первых, приведенные SQL запросы явно делают не то, чего вы хотите.
Вот правильные варианты:
select * from customers where (category = 4);

select * from customers where (category = 4 or category = 8);

select * from customers where (category = 4 or category = 8 or category = 9);


2. Если я прав, то цикл не нужен. Достаточно проверить одним предикатом:
var result = from c in customers where categories.Contains(c.category)


3. Linq идеально подходит для динамических решений в духе твоего цикла. Только фильтры нужно применять не к одному и тому же источнику, а цепочкой:
var result = from c in customers select c;
if (categories.Length > 0)
{
  result = from r in result where categories.Contains(r.category) select r;
}

if (!name.IsNullOrEmpty)
{
  result = from r in result where r.name == name select r;
}
...
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: LINQ - Динамическое построение запроса
От: J_K  
Дата: 17.07.08 11:58
Оценка:
Спасибо всем за интересные сообщения.
Да, конечно, там в запросе вместо and должен быть or. Сообщение писалось в час ночи, уже голова плоховато работала.

Буду исследовать предложенные вами варианты.
Вопрос к последнему оратору.
Вы писали:

var result = from c in customers select c;
if (categories.Length > 0)
{
  result = from r in result where categories.Contains(r.category) select r;
}

if (!name.IsNullOrEmpty)
{
  result = from r in result where r.name == name select r;
}


Эта конструкция и была мне нужна, но! Разве select не должен идти последним? Или это неважно?
Life is very short and there's no time
for fussing and fighting... (C) Paul McCartney & John Lennon
Re[2]: LINQ - Динамическое построение запроса
От: adontz Грузия http://adontz.wordpress.com/
Дата: 17.07.08 12:35
Оценка:
Здравствуйте, Sinclair, Вы писали:

Интересно какой это всё сгенерирует SQL...
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[3]: LINQ - Динамическое построение запроса
От: J_K  
Дата: 17.07.08 12:51
Оценка:
Здравствуйте, adontz, Вы писали:

A>Здравствуйте, Sinclair, Вы писали:


A>Интересно какой это всё сгенерирует SQL...


Мне тоже интересно. Но сейчас у меня SQL 2005, а там нет профайлера, чтобы посмотреть... или можно как-то по-другому?
Life is very short and there's no time
for fussing and fighting... (C) Paul McCartney & John Lennon
Re[4]: LINQ - Динамическое построение запроса
От: Idsa Россия  
Дата: 17.07.08 14:39
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>Здравствуйте, adontz, Вы писали:


J_K>Мне тоже интересно. Но сейчас у меня SQL 2005, а там нет профайлера, чтобы посмотреть... или можно как-то по-другому?

Как так нет? Есть. В Performance tools.
Можно и по-другому. Есть плагин к VS 2008 — SqlServerQueryVisualizer: http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx
Кстати в SP1 beta он уже включен.
... << RSDN@Home 1.2.0 alpha 4 rev. 1096>>
Re[5]: LINQ - Динамическое построение запроса
От: Lloyd Россия  
Дата: 17.07.08 15:28
Оценка: +1
Здравствуйте, Idsa, Вы писали:

I>Можно и по-другому. Есть плагин к VS 2008 — SqlServerQueryVisualizer: http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx

I>Кстати в SP1 beta он уже включен.

Не нужно никакого плагина:
context.Log = Console.Out;
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[2]: LINQ - Динамическое построение запроса
От: Аноним  
Дата: 17.07.08 17:28
Оценка:
Здравствуйте, Аноним, Вы писали:

А>посмотрите Dynamic Linq

Sinclair и Ziaw, напишите, пожалуйста, чем вам это решение не нравится.
Re[3]: LINQ - Динамическое построение запроса
От: Ziaw Россия  
Дата: 18.07.08 03:51
Оценка: +1
Здравствуйте, <Аноним>, Вы писали:

А>Здравствуйте, Аноним, Вы писали:


А>>посмотрите Dynamic Linq

А>Sinclair и Ziaw, напишите, пожалуйста, чем вам это решение не нравится.

1. Linq создан как средство избавления от динамической генерации текстовых запросов.
2. В данном случае нет необходимости в их применении.
3. Linq предоставляет досточно механизмов для генерации запросов проходящих проверку на уровне компиляции.
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[4]: Увы...
От: J_K  
Дата: 18.07.08 12:13
Оценка:
Попытка использовать Contains успехом не увенчалась. Возник эксепшн:

LINQ to Entities does not recognize the method 'Boolean Contains(Int32)' method, and this method cannot be translated into a store expression.

Судя по всему, это еще баг:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3377718&amp;SiteID=1
Life is very short and there's no time
for fussing and fighting... (C) Paul McCartney & John Lennon
Re[5]: Увы...
От: Lloyd Россия  
Дата: 18.07.08 16:28
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>Попытка использовать Contains успехом не увенчалась. Возник эксепшн:


J_K>LINQ to Entities does not recognize the method 'Boolean Contains(Int32)' method, and this method cannot be translated into a store expression.


Зато Linq2Sql может
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[4]: LINQ - Динамическое построение запроса
От: IT Россия linq2db.com
Дата: 19.07.08 03:16
Оценка: +1
Здравствуйте, J_K, Вы писали:

J_K>Мне тоже интересно. Но сейчас у меня SQL 2005, а там нет профайлера, чтобы посмотреть... или можно как-то по-другому?


В отладчике смотрим на result.ToString().
... << RSDN@Home 1.2.0 alpha rev. 771>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: Увы...
От: Idsa Россия  
Дата: 21.07.08 06:00
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>Судя по всему, это еще баг:

Не баг, конечно, просто реализовать не успели.
Сейчас попробовал заюзать Contains на VS 2008 SP1 beta (в составе с SP1 beta идет EF beta 4) — тоже не работает. Что-то затянули они... а возможность-то полезная.
... << RSDN@Home 1.2.0 alpha 4 rev. 1096>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.