为了账号安全,请及时绑定邮箱和手机立即绑定

在运行时使用属性名称和类型查询 IQueryable

在运行时使用属性名称和类型查询 IQueryable

C#
哔哔one 2023-05-14 16:13:27
我需要在运行时知道属性名称和类型来进行查询。我已经使用了反射IEnumerable<>但是会因此出现性能问题吗?我想知道是否有更好的方法来做到这一点IQueryable<>?我已经研究了一下,Expressions但我不太确定该怎么做。编辑:目前这似乎不是性能问题,但我还没有测试过非常大的工作负载。我需要搜索运行时已知的多个不同类型的字段。var cosmosClient = new DocumentClient(new Uri(cosmosDBEndpointUrl), cosmosDBAuthorizationKey);var feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };var objects = cosmosClient.CreateDocumentQuery<MyObject>(collectionLink, feedOptions).AsEnumerable();if (!string.IsNullOrEmpty(searchQuery)){             var predicate = PredicateBuilder.New<MyObject>();     foreach (var fieldToSearch in fieldsToSearch)    {      predicate = predicate.Or(x => x.GetPropertyValue(fieldToSearch).CheckDateTime().ToString()                                     .Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase));      objects = objects.Where(predicate);    }}objects = objects.Skip(index)                 .Take(pageSize);return objects.ToList();这个辅助方法:public static object GetPropertyValue(this object obj, string propertyName){    foreach (var part in propertyName.Split('.'))    {        if (obj == null) { return null; }        Type type = obj.GetType();        PropertyInfo info = type.GetProperty(part, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);        if (info == null) { return null; }        obj = info.GetValue(obj, null);    }    return obj;}我不知道在这种特殊情况下是否有更好的方法来做到这一点。
查看完整描述

1 回答

?
小怪兽爱吃肉

TA贡献1852条经验 获得超1个赞

所以在这种情况下,我确实认为你想要 IQueryable而不是IEnumerable. 最容易记住的方法是将IEnumerable所有数据加载到内存中,然后运行其余的 LINQ 查询,同时IQueryable尝试在源上运行过滤器(如果它可以将结果数据加载到内存中)。您确实需要使用支持IQueryableLINQ to SQL 或 Entity Framework 的 ORM。对于 CosmosDB,SDK 构建在 LINQ to SQL 之上。

通过使用你将所有IEnumerable数据从 Cosmos加载到内存中,然后应用你的过滤器和分页。当您只有几条记录时这很好,但是随着数据集的增长,您可能可以想象这会如何成为一个主要问题。通常,您希望在数据库中做尽可能多的工作,并且只返回最少数量的结果。这将远远超过使用反射构建谓词的任何性能考虑因素。

的最好的部分之一IQueryable是它继承自IEnumerable,因此任何与之一起工作的东西都IEnumerable将与IQueryable. 您需要做的就是摆脱您的AsEnumerable().


重要的是要注意,并非所有 LINQ 运算符都自动支持,一旦系统遇到无法翻译的运算符,它就会执行目前已有的操作,并在内存中完成其余操作。文档中有CosmosDB 的可用运算符列表。

对于您的查询,最重要的是,虽然您的Where()条款得到支持,Skip()Take()目前不支持。这意味着每次执行此方法时,所有结果都将从 CosmosDB 返回,然后对分页进行评估。

有几种方法可以在 SDK 中处理分页。当前支持的方式是将MaxItemCountinside your设置FeedOptions为您的页面大小。Skip()当前系统使用延续令牌代替函数。为了访问令牌,您可以使用AsDocumentQuery(). 由于您必须逐页浏览到下一页,因此缓存令牌可能非常有帮助——跳来跳去很困难。

第二种选择是使用 .Net SDK v3。它目前处于预览阶段,但可在 Nuget 上使用。几个月前启用了 skip/take。在这种情况下,在您调用之前的所有内容ToList()都应转换为 SQL 并在 CosmosDB 中进行评估。


查看完整回答
反对 回复 2023-05-14
  • 1 回答
  • 0 关注
  • 118 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信