2015-08-17 11 views
3

У меня есть база данных, на которой пользователи могут выполнять различные вычисления. Расчеты работают на 4 разных столбцах каждый расчет не обязательно использовать каждый столбец т.е. calculation1 может превратиться в SQL, какДинамически генерировать Linq Выбрать

SELECT SUM(Column1) 
FROM TABLE 
WHERE Column1 is not null 

и calculation2 бы

SELECT SUM(Column2) 
WHERE Column2 is null 

Я пытаюсь создать это с помощью LINQ и I может получить правильные данные обратно, вычисляя все каждый раз, когда такие, как

table.Where(x => x.Column1 != null) 
    .Where(x => x.Column2 == null) 
    .GroupBy(x => x.Date) 
    .Select(dateGroup => new 
      { 
       Calculation1 = dateGroup.Sum(x => x.Column1 != null), 
       Calculation2 = dateGroup.Sum(x => x.Column2 == null) 
      } 

проблема заключается в том, что мой набор данных очень велик, и поэтому я не хочу для выполнения расчета, если пользователь не запросил его. Я изучил динамическое создание запросов Linq. Все, что я нашел до сих пор, это PredicateBuilder и DynamicSQL, которые кажутся полезными только для динамического генерации предиката Where и hardcoding самого SQL-запроса как строки с суммированием Sum (Column1) или Sum (Column2), когда это необходимо.

Как можно динамически добавлять разные части запроса Select в анонимный тип, как это? Или я должен смотреть на совершенно другой способ обращения с этим

+0

Вместо динамического создания ваших запросов вы могли бы инкапсулировать их в свои собственные методы и вызвать методы условно? – Jerreck

+0

Взгляните на * Динамический LINQ *: http://dynamiclinq.azurewebsites.net/. Он позволяет использовать динамические предложения во многих операциях LINQ, включая 'Select'. –

+0

Ive немного посмотрел на Dynamic Linq. Я больше посмотрел на вашу ссылку, но избавление от безопасности типа кажется выбором, который я только хочу сделать, если мне нужно –

ответ

0

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

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

var query = table 
    .Where(x => x.Column1 != null) 
    .Where(x => x.Column2 == null) 
    .GroupBy(x => x.Date); 

Вы можете dyamically решить вопросы с любым из следующих способов:

  1. dynamic

    dynamic returnObject = new ExpandoObject(); 
    
    if (includeOne) 
        returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.Column1)); 
    
    if (includeTwo) 
        returnObject.Calculation2 = groupedQuery.Select (q => q.Sum (x => x.Column2)); 
    
  2. Бетонный Тип

    var returnObject = new StronglyTypedObject(); 
    if (includeOne) 
        returnObject.Calculation1 = groupedQuery.Select (q => q.Sum(x => x.BrandId)); 
    
  3. Dictionary<string, int>

+0

. Я вижу, как это будет работать, если я могу делать каждый расчет отдельно. Проблема в том, что мне придется делать разные группировки вычислений, которые я не буду знать, пока не появятся запросы пользователей. Я думал о жестком кодировании всех возможных комбинаций, но поскольку у них уже 14 возможных вычислений, и их может быть больше, это невозможно , Есть ли способ обойти это с вашим предложением? –

+0

Я думаю, что может быть, но было бы полезно, если бы вы могли описать, как будут поступать запросы. У вас есть образец? –

+0

В настоящее время, когда бэкэнд получает список запросов, есть несколько параметров, которые применяются к каждому расчету, например, к дате отчета, которые выполняются в предложении where и сгруппированы, как вы показали. Затем есть список имен вычислений и оператор switch, который захватывает данные для выбранных вычислений. Проблема в том, что для того, чтобы эти данные были доступны в инструкции switch, мне нужно, чтобы Linq уже вычислил все. Я не могу найти способ делать разные операторы Select на лету, не прибегая к жестко закодированным строкам в Dynamic Linq –

0

Я решил эту проблему, и держал себя от того, чтобы потерять безопасность типа с динамической Linq с помощью Hacky обходного пути. У меня есть объект, содержащий Bools, которые соответствуют тому, что расчеты я хочу сделать такие, как

public class CalculationChecks 
{ 
    bool doCalculation1 {get;set;} 
    bool doCalculation2 {get;set;} 
} 

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

Однако это похоже на краевой регистр с linq на sql или ef, что заставляет сгенерированный sql выполнять вычисления, указанные в DoCalculation1() и DoCalculation2, а затем использовать оператор case для определения того, собираюсь вернуть данные мне. Он работает значительно медленнее, 40-60% в тестировании, а план выполнения показывает, что он использует гораздо более неэффективный запрос.

Решение этой проблемы состояло в том, чтобы использовать ExpressionVisitor для прохождения выражения и удаления вычислений, если соответствующий bool был ложным. Код, показывающий, как реализовать этот ExpressionVisitor, предоставил @StriplingWarrior по этому вопросу Have EF Linq Select statement Select a constant or a function

Использование обоих этих решений вместе не создает sql, который работает со скоростью 100% от скорости простого sql. При тестировании он находился в пределах 10 с простого sql независимо от размера тестового набора, и основные части плана выполнения были одинаковыми.

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