LINQ / LINQ-to-SQL: есть ли способ генерировать SQL с использованием комбинированного фильтра «где»? - PullRequest
1 голос
/ 24 ноября 2010

Я пытаюсь настроить интерфейс запросов, где пользователь может выбрать произвольный набор фильтров для окончательного извлечения данных из базы данных.Проблема, с которой я сталкиваюсь, заключается в том, что LINQ-to-SQL либо ведет себя не так, как я ожидал, или я делаю что-то не так, потому что только первое из моих ограничивающих условий WHERE передается через 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);
}

Когда я запускаю это, мой результирующий набор - это то, что я ожидаю (т.е. активные сеансы с именем пользователя = "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 ]

3 голосов
/ 24 ноября 2010

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

Попробуйте:

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();
    }
}

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

2 голосов
/ 24 ноября 2010

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

1 голос
/ 24 ноября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...