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

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

посмотрите 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: 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 - Динамическое построение запроса
От: 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[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[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]: 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[3]: LINQ - Динамическое построение запроса
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.08.08 07:13
Оценка: -1
Здравствуйте, J_K, Вы писали:
J_K>Эта конструкция и была мне нужна, но! Разве select не должен идти последним?
Да. Он и так идет последним.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
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 - Динамическое построение запроса
От: Кэр  
Дата: 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 - Динамическое построение запроса
От: 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[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[2]: LINQ - Динамическое построение запроса
От: Аноним  
Дата: 17.07.08 17:28
Оценка:
Здравствуйте, Аноним, Вы писали:

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

Sinclair и Ziaw, напишите, пожалуйста, чем вам это решение не нравится.
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[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>>
Re[6]: Увы...
От: Idsa Россия  
Дата: 21.07.08 06:01
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Зато Linq2Sql может

К сожалению, он много чего другого не может
... << RSDN@Home 1.2.0 alpha 4 rev. 1096>>
Re[5]: Увы...
От: Ziaw Россия  
Дата: 21.07.08 06:33
Оценка:
Здравствуйте, 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]
... << RSDN@Home 1.2.0 alpha 4 rev. 0>>
Re[7]: Увы...
От: Lloyd Россия  
Дата: 21.07.08 07:42
Оценка:
Здравствуйте, Idsa, Вы писали:

L>>Зато Linq2Sql может

I>К сожалению, он много чего другого не может

Во многих случаях оно и не надо
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[8]: Увы...
От: Idsa Россия  
Дата: 21.07.08 07:58
Оценка:
Здравствуйте, Lloyd, Вы писали:

L>Во многих случаях оно и не надо

Хз, хз. Сейчас занимаюсь переводом DAL'а с Linq to Sql на Entity Framework... испытываю исключительно положительные эмоции
Как вспомню ту жесть, когда невозможно было обновить модель с измененными значениями, а проект развивается и развивается...
Да и вообще, много полезных плюшек появилось.
... << RSDN@Home 1.2.0 alpha 4 rev. 1096>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.