我使用以下函数使用我以编程方式构建的一堆查询表达式来查询EF6模型:
/// <summary>
/// Common application of generated queries to select the required resources
/// </summary>
/// <typeparam name="T">The EF model type</typeparam>
/// <param name="source">The source of models (a DbSet)</param>
/// <param name="queries">A collection of query pairs (database, local) to be applied as Where clauses</param>
/// <returns></returns>
protected static IEnumerable<string> ApplyCriteriaBase<T>(IQueryable<T> source, IEnumerable<(Expression, Expression)> queries) where T : DbResource
{
// Apply the database queries sequentially as Where clauses
var resources = queries
.Select(p => p.Item1)
.Aggregate(source, (ds, qry) => ds.Where(qry as Expression<Func<T, bool>>))
.Distinct();
// If there are no local queries then don't bother fetching the whole object, just select the id in the generated SQL
if(queries.All(q => q.Item2 == null))
return resources
.Select(a => a.Id.ToString());
// Apply local queries
// AsEnumerable() escapes from the IQueryable monad so the subsequent queries are executed locally on IEnumerable
// This requires fetching the whole object because we dont know which bit is to be tested by the local query
// Note that IEnumerable doesn't understand query expressions so we have to compile them to code and then invoke them
return queries
.Select(p => p.Item2)
.Where(q => q != null)
.Aggregate(
resources.ToList() as IEnumerable<T>, // .AsEnumerable() doesn't work
(ds, qry) => ds.Where((qry as Expression<Func<T, bool>>).Compile().Invoke))
.Select(a => a.Id.ToString());
}
这个想法是查询包含2元组的查询表达式,其中每个查询表达式中的第一个将针对数据库(MySql)运行,而第二个(如果存在)将针对返回的EF模型运行。 据我所知,AsEnumerable()应允许第二个查询在本地和懒惰地执行而不将所有结果都拉入内存。不幸的是,这失败了错误:
“已经有一个与此Connection关联的开放DataReader 必须先关闭。“
示例中显示的修复,将结果流转换为List,正常工作但严格。 为什么AsEnumerable()不起作用,是否存在另一种懒惰处理结果的方法? DbResource的一些实例非常大,我只想累积每个资源的Id。
谢谢, 安迪