Выполните .Select () в IQueryable с помощью списка делегатов Func <T, string> - PullRequest
3 голосов
/ 20 мая 2009

Прежде всего, позвольте мне извиниться, если название не имеет смысла. Мне трудно понять, что я делаю, не говоря уже о возможности использовать правильные слова для описания того, что я делаю.

Я создаю общий класс сетки, в котором я определяю столбцы комбинацией выражений header / linq:

public class Column<T>
{
    public string Header { get; set; }
    public Func<T, string> ValueExpression { get; set; }
}

Использование:

Columns = new List<Column<Employee>>
              {
                  new Column<Employee> {Header = "Employee Id", ValueExpression = e => e.EmployeeID.ToString()},
                  new Column<Employee> {Header = "Name", ValueExpression = e => e.FirstName + " " + e.LastName},
                  new Column<Employee> {Header = "Employee Birthday Year", ValueExpression = e => e.BirthDate.HasValue ? e.BirthDate.Value.Year.ToString() : ""}
              },

Я хочу проецировать выражение ValueExpression (Func<T, string>) на IQueryable «сотрудников»:

var db = new NorthwindDataContext();
var employees = db.Employees.Select(e => e);

Я могу заставить это работать, извлекая IEnumerable<IEnumerable<string>> из сотрудников (список списков строк, используемых в моих представлениях), как это:

var elementsList = employees.ToPagedList(PageIndex, PageSize);
var elementStringList = elementsList.ToStringList(Columns.Select(c => c.ValueExpression).ToArray());

Не обращайте внимания на материал PagedList, он не имеет значения и примерно такой же, как ToList ();

Вот метод расширения ToStringList ():

public static IEnumerable<IEnumerable<string>> ToStringList<T>(this IPagedList<T> enumerable, params Func<T, string>[] fields)
{
    foreach (var element in enumerable)
        yield return element.ToStringList(fields);
}

private static IEnumerable<string> ToStringList<T>(this T element, params Func<T, string>[] fields)
{
    foreach (var field in fields)
        yield return field(element);
}

Проблема состоит в том, что этот подход включает выполнение IQueryable перед указанием полей, которые должны быть возвращены.

В результате следующий запрос выполняется где-то по пути:

SELECT [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName], [t0].[Title], [t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[HomePhone], [t0].[Extension], [t0].[Photo], [t0].[Notes], [t0].[ReportsTo], [t0].[PhotoPath]
FROM [dbo].[Employees] AS [t0]

Как вы могли догадаться, нежелательно извлекать ВСЕ поля из таблицы Сотрудники.

Я ищу способ каким-то образом создать метод расширения для сотрудников IQueryable, в котором я могу передать список функций (членов столбца "ValueExpression") и "построить" новый IQuerable таким образом, который будет выполнить SQL, который просто извлекает необходимые поля из базы данных.

Итак, я ищу что-то вроде этого:

IQueryable employees = employees.SelectByExpressions(Columns.Select(c => c.ValueExpression).ToArray());

Заранее спасибо.

Ответы [ 4 ]

1 голос
/ 21 мая 2009

Основная проблема, с которой вы столкнетесь, заключается в том, что Select должен возвращать объект некоторого типа, но вы хотите, чтобы этот тип имел разные поля в зависимости от сгенерированного динамического запроса. Когда вы используете анонимные типы, компилятор генерирует для вас тип с соответствующими полями. Но в вашем случае вы не знаете во время компиляции, как будет выглядеть список полей, поэтому вы не можете заставить компилятор сгенерировать тип для вас.

Хотя, безусловно, возможно динамически создать тип, который будет содержать только те поля, которые вы хотите, я должен остановиться и спросить, нужно ли это. Частично оправдание использования O / R-сопоставителей, таких как LINQ-to-SQL, особенно в отличие от DataSets, заключается в том, что стоимость обслуживания кода, который динамически возвращает разные наборы полей в разное время, не стоит той незначительной экономии, которую вы получаете от запроса или из использования памяти. Фактически в некоторых случаях это может даже ухудшить производительность запросов, поскольку сервер базы данных не может оптимизировать несколько запросов с разными списками полей так же, как он может оптимизировать несколько запросов с одинаковыми списками полей.

Другим вариантом будет просто вернуть все поля, а отобразить только выбранные поля. Это, безусловно, будет проще и гораздо проще в обслуживании. Однако, если вы измерили влияние этого решения на производительность и определили, что оно не соответствует вашим требованиям, вы, безусловно, можете изучить динамическое создание необходимых типов. Если вам нужна помощь с этим решением, я мог бы подготовить образец для вас. Но если бы я был тобой, я был бы абсолютно уверен, что мне нужно идти по этому пути, прежде чем идти в этом направлении.

ОБНОВЛЕНИЕ: Другой вопрос здесь заключается в том, будет ли ваша ЕДИНСТВЕННАЯ цель с этими запросами состоять в том, чтобы отображать их в сетках или в каком-либо другом виде динамически связанного интерфейса. Если это так, то может быть так же легко пойти с неким решением «мешок свойств», где нет конкретного типа с конкретными полями, а просто контейнер для пар ключ / значение, аналогичный DataRow. , Однако, если вы пытаетесь динамически создавать типы, которыми затем манипулируют в коде, я настоятельно рекомендую это сделать. Техническая реализация может быть интересной, но обслуживание очень быстро становится кошмаром. Я был вынужден поддерживать такие приложения раньше, и если у меня будет выбор, я никогда не буду снова.

0 голосов
/ 21 мая 2009

Интересный вопрос. Я думаю, что это связано с этим:

Как создать дерево выражений LINQ для выбора анонимного типа

Вам в основном нужен динамический выбор.

0 голосов
/ 21 мая 2009

Смотрели ли вы на Dynamic Linq. Я верю, что он делает то, что вы пытаетесь сделать.

Если у вас есть столбцы в виде строк, вы можете использовать это для выбора предиката select, и он будет иметь только те столбцы в SQL, который выполняется.

0 голосов
/ 20 мая 2009

Я не уверен, что полностью понимаю, что вы хотите сделать, но это выглядит подозрительно как Predicate Builder

Если нет, это может помочь вам в правильном направлении ...

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