Здравствуйте, 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>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, J_K, Вы писали:
J_K>помогите разобраться. У меня на сайте пользователь может выбрать некоторые критерии, в соответствии с которыми производится выборка. Поскольку критериев много, решено было отказаться от ХП и использовать только LINQ.
Linq тут вряд ли поможет, я бы сгенерировал все нужные варианты ХП автоматом, назвал бы и по каким-то правилом и при выборке генерировал бы имя ХП по тем же правилам, чтобы найти нужную.
Связность, конечно, высокая, но альтернативой является, пожалуй, только навороченная O/R M
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.
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, Аноним, Вы писали:
А>>посмотрите Dynamic Linq А>Sinclair и Ziaw, напишите, пожалуйста, чем вам это решение не нравится.
1. Linq создан как средство избавления от динамической генерации текстовых запросов.
2. В данном случае нет необходимости в их применении.
3. Linq предоставляет досточно механизмов для генерации запросов проходящих проверку на уровне компиляции.
Здравствуйте, J_K, Вы писали:
J_K>Мне тоже интересно. Но сейчас у меня SQL 2005, а там нет профайлера, чтобы посмотреть... или можно как-то по-другому?
В отладчике смотрим на result.ToString().
... << RSDN@Home 1.2.0 alpha rev. 771>>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте,
помогите разобраться. У меня на сайте пользователь может выбрать некоторые критерии, в соответствии с которыми производится выборка. Поскольку критериев много, решено было отказаться от ХП и использовать только 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
Здравствуйте, J_K, Вы писали:
J_K>Как заставить его работать? Может, вообще как-то по-другому это делается?
Поставьте себя на место LINQ — как можно из вашего foreach сгенерировать хотя бы минимально эффективный SQL запрос?
Попробуйте сделать Join для таблиц customers и categories
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
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 переприсвается. Смысла в этом не очень много.
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));
Спасибо всем за интересные сообщения.
Да, конечно, там в запросе вместо 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
Здравствуйте, 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[2]: LINQ - Динамическое построение запроса
От:
Аноним
Дата:
17.07.08 17:28
Оценка:
Здравствуйте, Аноним, Вы писали:
А>посмотрите Dynamic Linq
Sinclair и Ziaw, напишите, пожалуйста, чем вам это решение не нравится.
Здравствуйте, 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.
Здравствуйте, J_K, Вы писали:
J_K>Судя по всему, это еще баг:
Не баг, конечно, просто реализовать не успели.
Сейчас попробовал заюзать Contains на VS 2008 SP1 beta (в составе с SP1 beta идет EF beta 4) — тоже не работает. Что-то затянули они... а возможность-то полезная.
Здравствуйте, 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.
Значит надо самостоятельно создать нужный expression.
Например в виде цепочки c.CategoryId = 1 || c.CategoryId = 2 || c.CategoryId = n.
Ее LINQ to Entities понять просто обязан.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
namespace LinqDynamicExpression
{
class Program
{
static void Main(string[] args)
{
using (var dc = new TestDataContext())
{
dc.Customers.DeleteAllOnSubmit(dc.Customers);
dc.Customers.InsertAllOnSubmit(new[]
{
new Customer {CategoryId = 1},
new Customer {CategoryId = 3},
new Customer {CategoryId = 10},
new Customer {CategoryId = 11},
});
dc.SubmitChanges();
dc.Log = Console.Out;
var categories = new[] {1, 2, 3};
var result = dc.Customers
.Where(categories.BuildContainsAnalogue<Customer, int>(c => c.CategoryId))
.ToList();
Debug.Assert(result.Count == 2);
Debug.Assert(result.All(c => categories.Contains(c.CategoryId)));
}
}
}
public static class ContainsAnalogue
{
public static Expression<Func<T, bool>> BuildContainsAnalogue<T, C>(
this IEnumerable<C> collection,
Expression<Func<T, C>> propertyLambda)
{
var propertyGetter = propertyLambda.Body;
var firstElem = Expression.Constant(collection.First(), typeof(C));
var orChain = Expression.Equal(propertyGetter, firstElem);
foreach (var item in collection.Skip(1))
{
var curElem = Expression.Constant(item, typeof(C));
orChain = Expression.Or(orChain, Expression.Equal(propertyGetter, curElem));
}
return Expression.Lambda<Func<T, bool>>(orChain, propertyLambda.Parameters);
}
}
}
Запрос в данном случае получается такой.
SELECT [t0].[Id], [t0].[CategoryId]
FROM [dbo].[Customer] AS [t0]
WHERE ([t0].[CategoryId] = @p0) OR ([t0].[CategoryId] = @p1) OR ([t0].[CategoryId] = @p2)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p2: Input Int (Size = 0; Prec = 0; Scale = 0) [3]
Здравствуйте, Lloyd, Вы писали:
L>Во многих случаях оно и не надо
Хз, хз. Сейчас занимаюсь переводом DAL'а с Linq to Sql на Entity Framework... испытываю исключительно положительные эмоции
Как вспомню ту жесть, когда невозможно было обновить модель с измененными значениями, а проект развивается и развивается...
Да и вообще, много полезных плюшек появилось.