【问题标题】:How to Dynamically create Expression<Func<>> for count and group by clause如何为 count 和 group by 子句动态创建 Expression<Func<>>
【发布时间】:2020-08-13 10:22:56
【问题描述】:

我需要为以下查询创建动态 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);

但是对于获取Count 和对于Group By 条款如何做到这一点。

【问题讨论】:

标签: c# azure-cosmosdb predicate generic-lambda dynamic-expression


【解决方案1】:

我不熟悉 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
}

编辑:

我在 cmets 中的回答示例 ->

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 非常动态地构建谓词。 我可能错过了你想要做的事情,但我看不出在这里使用表达式树有什么好处。

CosmoDB 的 IQueryProvider 已经在为您翻译所有查询,因此除非我不知道某些事情,否则您通过自己将表达式映射到查询来编写不必要且可能不太优化的实现。

【讨论】:

  • 我们如何使用表达式构建器来做到这一点,就像在我们使用它来创建通用谓词的项目中一样。?
  • 好吧,我不确定你到底在做什么,但是 IQueryable 的 .Where() 方法已经采用了一个通用谓词,所以如果你使用这种方法而不是构建表达式树,我假设你已经拥有了你想要的东西?
【解决方案2】:

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


有一个使用字符串的 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)");

【讨论】:

  • 我必须使用表达式构建器,就像我们使用它来创建通用谓词的代码一样。
  • 您能否举例说明您是如何使用这些谓词的?你的想法是在 where 子句中有一个子查询吗?
【解决方案3】:

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

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


更新

但是,如果我正确理解您的动机,您实际上是在尝试根据您从库中获得的基本 IQueryable 动态构建一个带有 where 子句、选择、groupby 和/或计数的 IQueryable,是吗?对吗?

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

【讨论】:

  • 是的,你说得对。我也想这样做。将尝试您的建议并通知您。
  • 我试过了,但没用,你能给我发个例子吗?
猜你喜欢
  • 2011-07-02
  • 2014-12-26
  • 1970-01-01
  • 1970-01-01
  • 2010-10-25
  • 1970-01-01
  • 2011-04-24
  • 2016-01-10
  • 1970-01-01
相关资源
最近更新 更多