如何动态创建表达式<Func<>> 用于计数和分组依据子句



我需要为下面的查询创建动态lambda表达式谓词,以便在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);

但对于得到CountGroup By条款如何做到这一点。

我不熟悉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<gt;的CosmoDB已经在为您翻译所有的查询,所以除非我不知道什么,否则您正在通过自己将表达式映射到查询来编写不必要的、可能优化程度较低的实现。

我相信您可以为此使用动态Linq:


  • https://dynamic-linq.net/
  • https://www.nuget.org/packages/System.Linq.Dynamic.Core

有一个GroupBy扩展,它接受一个字符串:https://dynamic-linq.net/basic-query-operators#GroupBy

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)");

CountGroupBy是方法。因此,您首先需要使用反射来获取相应的MethodInfos(请注意从Queryable类而不是Enumerable类中获取方法,以及正确的重载!(。

然后使用Expression.Call(null, methodInfo, queryable)创建一个方法调用表达式。(第一个参数应该是null,因为这两个方法都是静态的。(这里,queryable是到目前为止您已经构建的IQueryable,您希望分别在其上调用CountGroupBy。对于GroupBy,您还需要添加一个额外的参数,这是用于投影City属性的lambda表达式——我想您知道如何构建它。


更新

但如果我正确理解你的动机,你实际上是在试图根据从库中获得的基本IQueryable,动态构建一个IQueryable,其中包含where子句、select子句、groupby子句和/或count子句,这是正确的吗?

在这种情况下,不应该CountGroupBy表示为表达式。相反,您应该在基础IQueryable上动态调用real方法WhereSelectCountGroupBy(以获得另一个IQueryable(,但要为这些方法的参数动态构建lambda表达式。最后,您将获得一个IQueryable,Cosmos DB LINQ提供商可以对其进行评估。

最新更新