猿问

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

我需要在运行时知道属性名称和类型来进行查询。我已经使用了反射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;

}

我不知道在这种特殊情况下是否有更好的方法来做到这一点。


哔哔one
浏览 119回答 1
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 中进行评估。
随时随地看视频慕课网APP
我要回答