2010-11-23 1 views
1

Я пытаюсь настроить интерфейс запросов, в котором пользователь может выбрать произвольный набор фильтров, чтобы в конечном итоге извлечь данные из базы данных. Проблема, с которой я сталкиваюсь, заключается в том, что LINQ-to-SQL либо не ведет себя так, как я ожидал, либо я делаю что-то неправильно, потому что только первый из моих ограничивающих условий WHERE передается через SQL. Другое фильтрующее вещество, похоже, происходит правильно, но, насколько я могу судить, это происходит, как только данные уже получены.LINQ/LINQ-to-SQL: Есть ли способ генерировать SQL, используя объединенный фильтр «где»?

Вот что я пытаюсь сделать.

У меня есть база данных, созданная с помощью таблицы Sessions. Я настроил мой LINQ-to-SQL отображения вещи, и я выборка «активных» сеансов отфильтровывая неопределенные времена выхода из системы, а также любые дополнительные фильтры пользователь может указать:

public static IEnumerable<Session> GetSessions(params Func<Session, bool>[] filters) 
{ 
    // I've created an auto-generate Linq-to-SQL context object with a Sessions table. 
    using (DataSourceDataContext ctx = new DataSourceDataContext()) 
    { 
     // Begin with all "active" sessions 
     IEnumerable<Session> sessions = ctx.Sessions.Where(x => x.LogoutTime == null); 
     foreach (var filter in filters) 
      sessions = sessions.Where(filter); 
     return sessions.ToArray(); 
    } 
} 

static void Main(string[] args) 
{ 
    Func<Session, bool> mySessions = (x => x.UserName == "steven"); 
    IEnumerable<Session> sessions = GetSessions(mySessions); 
} 

When Я бегу это, мой результирующий набор, что я ожидаю (т.е. активных сеансов с именем пользователя = «Steven»), но полный список активных сессий было предложено от SQL (с помощью Profiler):

SELECT [t0].[ID], [t0].[UserName], [t0].[LoginTime], [t0].[LogoutTime], [t0].[Location] 
FROM [dbo].[Sessions] AS [t0] 
WHERE [t0].[LogoutTime] IS NULL 

Я был под впечатление LINQ будет продолжать строить запрос за кулисами, пока я не повторю свою коллекцию, но я не могу понять, почему это не делает для части SQL. Я что-то упускаю, или просто пытаюсь заставить LINQ-to-SQL делать то, что он не собирается делать?

ответ

3

Вы используете IEnumerable<T> - это довольно-много ограничивается обработкой данных локально, когда материализуется ваш единственный .Where(...) на LogoutTime; состав запроса требуетIQueryable<T>. Это, в свою очередь, потребует Expression<Func<Session, bool>> для каждого предиката.

Try:

public static Session[] GetSessions(
    params Expression<Func<Session, bool>>[] filters) 
{ 
    // I've created an auto-generate Linq-to-SQL context object with 
    // a Sessions table. 
    using (var ctx = new DataSourceDataContext()) 
    { 
     // Begin with all "active" sessions 
     IQueryable<Session> sessions = ctx.Sessions 
      .Where(x => x.LogoutTime == null); 
     foreach (var filter in filters) 
      sessions = sessions.Where(filter); 
     return sessions.ToArray(); 
    } 
} 

Вы должны все еще быть в состоянии назвать это с лямбдах, но если вы явно обработки Func<Session, bool> делегатов те должны быть обновлены для выражения деревьев, через Expression<Func<Session, bool>>.

+0

Это сработало. Большое вам спасибо! Ваше объяснение также помогает, потому что я подумал о различии между IQueryable и IEnumerable (и я только что обнаружил выражения). – Steven 2010-11-23 22:17:41

2

Вы присваиваете результат первого оператора ctx.Sessions.Where() переменной IEnumerable<Session>. Это приводит к тому, что последующие операции фильтрации используют метод расширения Where в IEnumerable, а не IQueryable. Метод Queryable.Where - тот, для которого поставщик Linq Linq2SQL может переводить предикаты фильтра (типа Expression> в фактический SQL. Enumerable.Where принимает делегат Func и выполняет фильтрацию в памяти

1

использует IQueryable вместо IEnumerable

Смежные вопросы