Linq To SQL Выберите динамические столбцы

Можно ли динамически ограничить количество столбцов, возвращаемых из запроса LINQ to SQL?

У меня есть SQL-представление базы данных с более чем 50 столбцами. Мое приложение имеет объект домена с более чем 50 свойствами, по одному для каждого столбца. В моем проекте winforms я привязываю список объектов домена к сетке. По умолчанию отображаются только некоторые из столбцов, однако пользователь может включать/выключать любой из столбцов.

Пользователи жалуются, что сетка занимает слишком много времени для загрузки. Я захватил созданный LINQ SQL-запрос, затем выполнил его в SQL Server Management Studio и проверил его медленный. Если я изменяю инструкцию SQL, удаляя все невидимые столбцы, она выполняется почти мгновенно. Прямая корреляция между производительностью и количеством столбцов в запросе.

Мне интересно, возможно ли его динамическое изменение количества столбцов, возвращаемых из SQL-запроса, сгенерированного LINQ? Например, вот как выглядит мой код:

public List<Entity> GetEntities()
{
    using (var context = new CensusEntities())
    {
        return (from e in context.Entities
            select e).ToList();
    }
}

Объект context.Entities был сгенерирован из представления SQL, содержащего более 50 столбцов, поэтому при выполнении выше он генерирует SQL, как "SELECT Col1, Col2, Col3,... Col50 FROM Entity INNER JOIN...". Я хотел бы изменить подпись метода, чтобы она выглядела так:

public List<Entity> GetEntities(string[] visibleColumns)
{
    using (var context = new CensusEntities())
    {
        return (from e in context.Entities
            select e).ToList();
    }
}

Я не уверен, как изменить тело этого метода, чтобы изменить сгенерированный оператор SQL, чтобы возвращать значения столбца, о которых я забочусь, все остальные могут быть NULL.

Ответ 1

Что-то вроде этого должно работать:

 List<string> columns = new List<string>();
 columns.Add("EmployeeID");
 columns.Add("HireDate");
 columns.Add("City");

Добавить столбцы в ваш список ^.

var result = Class.ReturnList(columns);  

Передайте список методу ^.

public static List<Entity> ReturnList(List<string> VisibleColumns)
        {
            StringBuilder SqlStatement = new StringBuilder();
            SqlStatement.Append("Select ");
            for (int i = 0; i < VisibleColumns.Count; i++)
            {
                if (i == VisibleColumns.Count - 1)
                {
                    SqlStatement.Append(VisibleColumns[i]);
                }
                else
                {
                    SqlStatement.Append(VisibleColumns[i]);
                    SqlStatement.Append(",");
                }
            }
            SqlStatement.Append(" FROM Entity");
            using (var ctx = new DataClasses1DataContext())
            {
                var result = ctx.ExecuteQuery<Entity>(SqlStatement.ToString());
                return result.ToList();
            }

        }

В основном это просто оператор SELECT со всеми полями, которые вы передали с помощью списка VisibleColumns.

В этом случае оператор SQL, который будет сгенерирован строками в списке VisibleColumns:

Select EmployeeID, HireDate, City From Employee

(примечание: я использовал базу данных Northwind, чтобы попробовать это, поэтому имена столбцов EmployeeID и т.д. Вы должны заменить их своим собственным, очевидно.)

Ответ 2

Это не тривиально делать это динамически, но если у вас есть ограниченный набор комбинаций столбцов, которые вы хотите восстановить, вы можете сделать явный выбор следующим образом:

public List<Entity> GetEntities()
{
    using (var context = new CensusEntities())
    {
        return (from e in context.Entities
            select new
            {
                col1 = e.col1,
                col4 = e.col4,
                col5 = e.col5,
            }
        ).ToList()
        .Select(x=>new Entity{col1 = x.col1, col4 = x.col4, col5 = x.col5}).ToList();
    }
}

Дополнительный шаг выбора необходим, потому что LINQ2SQL не будет создавать для вас частичные объекты.

Создайте метод для каждой общей комбинации столбцов (особенно исходных), которые пользователи хотят получить.

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

public List<Entity> GetEntities()
{
    using (var context = new CensusEntities())
    {
        var combinedResult = (from e in context.Entities
            select new {
                Entity = e,
                CollectedValues = new
                                  {
                                      // Insert default values of the correct type as placeholders
                                      col1 = 0, // or "" for string or false for bool
                                      col2 = 0, // or "" for string or false for bool
                                      // ...
                                      col49 = 0, // or "" for string or false for bool
                                      col50 = 0, // or "" for string or false for bool
                                  }
        );

        // Then copy each requested property

        // col1
        if (useCol1)
        {
            var combinedResult = (from e in combinedResult
                select new {
                    Entity = e,
                    CollectedValues = new
                                      {
                                          col1 = e.Enitity.col1, // <-- here we update with the real value
                                          col2 = e.CollectedValues.col2, // <-- here we just use any previous value
                                          // ...
                                          col49 = e.CollectedValues.col49, // <-- here we just use any previous value
                                          col50 = e.CollectedValues.col50, // <-- here we just use any previous value                                          }
            );
        }

        // col2
        if (useCol2)
        {
         // same as last time
                                          col1 = e.CollectedValues.col1, // <-- here we just use any previous value
                                          col2 = e.Enitity.col2, // <-- here we update with the real value
                                          // ...
        }

        // repeat for all columns, update the column you want to fetch

        // Just get the collected objects, discard the temporary
        // Entity property. When the query is executed here only
        // The properties we actually have used from the Entity object
        // will be fetched from the database and mapped.
        return combinedResult.Select(x => x.CollectedValues).ToList()
        .Select(x=>new Entity{col1 = x.col1, col2 = x.col2, ... col50 = x.col50}).ToList();
    }
}

Будет много кода и боль для поддержания, но он должен работать.
Если вы идете по этому маршруту, я предлагаю вам создать генератор кода, который создает этот код с отражением от вашего контекста LINQ.

Ответ 3

Попробуйте что-то вроде этого

using (var context = new CensusEntities())
{
    var q = from e in context.Entities
        select e.myfield1,e.myfield2;
    return q.Tolist();
}

Полученный запрос должен быть легче, а также все преобразование данных, которое находится ниже. Но если вам действительно нужно создать динамический ввод, я думаю, что нужно задействовать динамический sql. так 1. Создайте динамический sql и получите таблицу данных 2. используйте преобразование динамического объекта в виде данных, как показано здесь. Как преобразовать DataTable в объект Dynamic? btw много тяжелой работы, я думаю, вы должны рассмотреть использование первого блока кода.