2016-03-23 6 views
5

Учитывая список IQueryables, как вы можете суммировать количество каждого из них, не имея нескольких операторов, выполняемых в базе данных?Сумма Список IQueryable

return queries 
       .Sum(qy=> qy.Count()); 

Вышеупомянутые работы, но попадают в базу данных по каждому запросу.

+0

ли вы найти решение? –

+0

@AdrianIftode: нет, и всего лишь 11 просмотров (вероятно, половина от меня), я не очень надеюсь на stackoverflow, чтобы помочь. – jmoreno

+0

щедрость должна исправить это :) –

ответ

4

Вы можете сначала использовать функцию Aggregate с Concat объединить IQueryable, а затем Count общее как это:

return queries.Aggregate((x,y) => x.Concat(y)).Count() 
+0

Не хотите ли вы добавить '.Sum()' где-нибудь ?. Кроме того, выполняется ли один удаленный БД или один для каждого IQueryable? – Igor

+0

@Igor - Нет, поскольку вы объединяете все запросы в один, используя 'Concat' (который переводится как' UNION ALL'), вам просто нужно подсчитать записи объединенного запроса – Aducci

+0

Он будет выполняться только один раз, потому что 'Aggregate 'возвращает только один' IQueryable', и запрос отправляется только в базу данных, как только вы вызываете 'Count()' – Aducci

1

Если вы используете Entity Framework, вы можете использовать расширение, называемое EntityFramework.Extended. Существует встроенное расширение под названием Future Queries. Это позволит вам указать, что запрос должен быть выполнен при следующем запуске поездки в базу данных. Команда

NuGet:

Install-Package EntityFramework.Extended 

Пример кода:

static void Main(string[] args) 
{ 
    using (var context = new MyDbContext()) 
    { 
     var modelSet1 = context.Models.Where(x => x.ModelId < 25).FutureCount(); 
     var modelSet2 = context.Models.Where(x => x.ModelId > 25 && x.ModelId < 32).FutureCount(); 
     var modelSet3 = context.Models.Where(x => x.ModelId > 32).FutureCount(); 

     var queries = new [] {modelSet1, modelSet2, modelSet3}; 

     var countQueries = queries.Sum(x => x.Value); 
     Console.WriteLine(countQueries); 
    } 

    Console.ReadLine(); 
} 
+1

Пока не удалось проверить эти работы, но это похоже на то, что я надеялся сделать. Я думаю/думал, что возможно, с помощью ограничений обычных методов linq, но это определенно жизнеспособно. Благодарю. – jmoreno

+0

@jmoreno - рад слышать. Дайте мне знать, если у вас есть другие вопросы. – Igor

+0

Я могу сказать, что это не работает с Linq2SQL, хотя это похоже на правильное решение: граф, возвращающий IQueryable –

2

Начиная от этой идеи Sum(q1,q2) = q1.Concat(q2).Count() я проверил следующие расширения:

public static class LinqExtensions 
{ 
    public static IQueryable<object> ConcatAny<T,R>(this IQueryable<T> q1, IQueryable<R> q2) 
    { 
     return q1.Select(c=>(object)null).Concat(q2.Select(c=>(object)null)); 
    } 

    public static IQueryable<object> ConcatAll(this IEnumerable<IQueryable<object>> queries) 
    { 
     var resultQuery = queries.First(); 
     foreach (var query in queries.Skip(1)) 
     { 
      resultQuery = resultQuery.ConcatAny(query); 
     } 
     return resultQuery; 
    } 
} 

Я предположил, что вы имеете гетерогенные запросы например IQueryable<T>, IQueryable<R> и т.д. и вы заинтересованы в подсчете всех строк, независимо от источника.

Таким образом, вы можете использовать эти расширения, как

var q1 = Table1.AsQueryable(); 
var q2 = Table2.AsQueryable(); 
var q3 = Table3.AsQueryable(); 

var queries = new IQueryable<object>[] {q1,q2,q3}; // we use here the covariance feature 

return queries.ConcatAll().Count(); 

сгенерированного SQL может выглядеть следующим образом

SELECT COUNT(*) AS [value] 
FROM (
    SELECT NULL AS [EMPTY] 
    FROM (
     SELECT NULL AS [EMPTY] 
     FROM [Table1] AS [t0] 
     UNION ALL 
     SELECT NULL AS [EMPTY] 
     FROM [Table2] AS [t1] 
     ) AS [t2] 
    UNION ALL 
    SELECT NULL AS [EMPTY] 
    FROM [Table3] AS [t3] 
    ) AS [t4] 

Я не думаю, что это очень эффективно, хотя

1

Хорошо, несколько минут поздно, но я понял! Вот код:

public static class LinqExtensions 
    { 
     public static int CountAll(this IEnumerable<IQueryable<object>> queries) 
     { 
      if (queries == null || !queries.Any()) 
      { 
       throw new ArgumentException("Queries parameter cannot be null or empty"); 
      } 
      Expression ex = Expression.Constant(0); 
      foreach (var qy in queries) 
      { 
       // create count expression 
       var expression = Expression.Call(
        typeof(Queryable), 
        "Count", 
        new[] { qy.ElementType }, 
        qy.Expression 
       ); 
       ex = Expression.Add(ex, expression); 
      } 
      return queries.First().Provider.Execute<int>(ex); 
     } 
    } 

Вы можете использовать его в качестве queries.CountAll() где запросы является IEnumerable<IQueryable<object>> как в ответ Эдриана или даже просто IEnumerable<IQueryable>.

Вот результат выборки SQL из профилировщика:

exec sp_executesql N'SELECT @p0 + ((
    SELECT COUNT(*) 
    FROM [A] AS [t0] 
    WHERE [t0].[i1] >= @p1 
    )) + ((
    SELECT COUNT(*) 
    FROM [B] AS [t1] 
    WHERE [t1].[i2] >= @p2 
    )) + ((
    SELECT COUNT(*) 
    FROM [C] AS [t2] 
    WHERE [t2].[i3] >= @p3 
    )) AS [value]',N'@p0 int,@p1 int,@p2 int,@p3 int',@p0=0,@p1=2,@p2=2,@p3=2 

Который является представление

var a = db.GetTable<A>(); 
    var b = db.GetTable<B>(); 
    var c = db.GetTable<C>(); 

    var q1 = a.Where(v => v.i1 >= 2); 
    var q2 = b.Where(v => v.i2 >= 2); 
    var q3 = c.Where(v => v.i3 >= 2); 

    var queries = new IQueryable<object>[] { 
     q1,q2,q3 
    }; 

Заметим, что А, В и С являются различные объекты/таблицы с различным числом свойств/columns и что выражения являются случайными, если фильтры.

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