我需要在运行时知道属性名称和类型的情况下进行查询。我在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;
}
在这种特殊情况下,我不知道是否有更好的方法。
答案 0 :(得分:1)
因此,在这种情况下是I do think you want IQueryable
而不是IEnumerable
。最容易记住的方法是,IEnumerable
将所有数据加载到内存中,然后运行其余的LINQ查询,而IQueryable
尝试在源上运行过滤器(如果可以随后将结果数据加载到其中)记忆。您确实需要使用支持IQueriable
的ORM,例如LINQ to SQL或Entity Framework。对于CosmosDB,SDK是基于LINQ to SQL构建的。
通过使用IEnumerable
,您将所有 all 数据从Cosmos加载到内存中,然后应用过滤器和分页。很好,虽然您只有几条记录,但是随着数据集的增长,您可能可以想象这将如何成为一个主要问题。通常,您希望在数据库中做尽可能多的工作,而只返回最少数量的结果。通过使用反射来构建谓词,这将大大超过任何性能方面的考虑。
IQueryable
最好的部分之一是它继承自IEnumerable
,因此与IEnumerable
兼容的任何内容都将与IQueriable
兼容。您需要做的就是摆脱AsEnumerable()
。
重要的是要注意,并不是所有的LINQ运算符都会自动得到支持,并且一旦系统命中了它就无法翻译,它将执行到目前为止所执行的操作,并执行其余的操作。文档中有list of available operators用于CosmosDB。
对于您的查询,最大的问题是,尽管支持您的Where()
子句,但目前不支持Skip()
和Take()
。这意味着每次执行此方法时,所有结果都将从CosmosDB返回,然后将评估分页。
SDK中有几种处理分页的方法。当前支持的方法是将MaxItemCount
中的FeedOptions
设置为页面大小。当前系统代替了Skip()
函数,而是使用延续令牌。为了访问令牌,您可以使用AsDocumentQuery()
。由于您必须单步浏览到下一页,因此缓存令牌可能非常有帮助-很难跳转。
第二个选项是使用.Net SDK的v3。当前处于预览状态,但available on Nuget。几个月前skip/take was enabled。在这种情况下,直到调用ToList()
之前的所有内容都应转换为SQL并在CosmosDB中进行评估。