Как динамически создавать Expression <Func <>> для предложения count и group by
Мне нужно создать динамический предикат лямбда-выражения для запроса ниже в Cosmos DB.
*select City, COUNT(City) as CityCount FROM Cities where status="Active"
group by City*
Раньше я создавал для равной работы вот такую
var parameter = Expression.Parameter(typeof(T), "t");
MemberExpression expBody = Expression.Property(parameter, columnName);
Но для получения Count
и для Group By
Пункт, как это сделать.
3 ответа
Я не знаком с CosmoDB, но разве LINQ не стал бы для этого проще?
IQueryable<City> query
= cityContainer.GetItemLinqQueryable<City>()
.Where(city => city.Status == "Active")
.GroupBy(city => city);
using FeedIterator<City> iterator = query.ToFeedIterator<City>();
int cityCount = 0;
for (;iterator.HasMoreResults; cityCount++)
{
City city = await setIterator.ReadNextAsync();
// do stuff
}
РЕДАКТИРОВАТЬ:
Примеры к моему ответу в комментариях ->
struct City
{
public string Name { get; set; }
public int Population { get; set; }
}
IQueryable<City> cities = default;
Func<City, bool> HasNastyName = c => c.Name.Length > 25;
Func<City, bool> IsMassive = c => c.Population > 500;
var nastyNameCities = cities.Where(HasNastyName);
var massiveCities = cities.Where(IsMassive);
Принимая во внимание дополнительную возможность их создания в целом, вы можете довольно динамично строить предикаты с помощью LINQ. Возможно, мне не хватает того, что вы пытаетесь сделать, но я не вижу никаких преимуществ в использовании здесь деревьев выражений.
IQueryProvider<> CosmoDB уже переводит для вас все запросы, поэтому, если я чего-то не знаю, вы пишете ненужные и, возможно, менее оптимизированные реализации, самостоятельно сопоставляя выражения с запросами.
Я считаю, что для этого можно использовать динамический Linq:
Существует расширение GroupBy, которое принимает строку:https://dynamic-linq.net/basic-query-operators
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
IQueryable<MyOb> testData = new EnumerableQuery<MyOb>(new List<MyOb>());
var query = testData
.Where(o => o.Active)
.GroupBy("City")
.Select("new (Key as City, Count() as CityCount)");
Console.WriteLine("Hello World!");
}
}
public class MyOb
{
public string City { get; set; }
public bool Active { get; set; }
}
}
Документация Dynamic Linq предоставляет этот пример
var result = context.Posts.GroupBy("BlogId").Select("new(Key, Count() AS Count)");
Count
а также GroupBy
методы. Итак, сначала нужно получить соответствующийMethodInfo
s с помощью отражения (позаботьтесь о том, чтобы получить методы из Queryable
класс, а не Enumerable
, и правильная перегрузка!).
Затем создайте выражение вызова метода, используя Expression.Call(null, methodInfo, queryable)
. (Первый параметр должен бытьnull
поскольку оба метода статичны.) Здесь queryable
это IQueryable
вы уже достигли этого момента, на котором вы хотите позвонить Count
или GroupBy
соответственно. ЗаGroupBy
, вам также необходимо добавить дополнительный параметр, который является лямбда-выражением для проецирования City
собственность - я полагаю, вы знаете, как это построить.
Обновить
Но если я правильно понимаю вашу мотивацию, вы на самом деле пытаетесь динамически построить IQueryable
с предложениями where, select, groupby и / или counts на основе базы IQueryable
вы получаете из библиотеки, это правильно?
В этом случае не следует выражатьCount
а также GroupBy
как выражения. Скорее вы должны динамически вызывать реальные методыWhere
, Select
, Count
а также GroupBy
на твоей базе IQueryable
(чтобы получить другой IQueryable
), но динамически строить лямбда-выражения для параметров этих методов. В итоге у вас будетIQueryable
которую может оценить поставщик LINQ Cosmos DB.