Applying IEnumerable<'T>.OrderBy on a IQueryable&l

本文关键字:on IQueryable OrderBy IEnumerable lt Applying gt | 更新日期: 2023-09-27 18:15:24

我有一个自定义类型,我在我的存储库中使用它来帮助我显式地表示查询选项,例如排序,分页等。

最初,界面看起来是这样的:

public class IQueryAction<TEntity> {
    IQueryable<TEntity> ApplyTo(IQueryable<T> entitity);
}

我可以这样表示排序:

public class SortingAction<TEntity, TSortKey> : IQueryAction<TEntity>
{
    public Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> SortAction { get; }
    public SortingAction(Expression<Func<TEntity, TSortKey>> sortExpression) {
        SortAction = q => q.OrderBy(sortExpression);
    }
    public IQueryable<TEntity> ApplyTo(IQueryable<TEntity> query) {
        return SortAction(query);
    }
}

大多数时候我使用实体框架,所以这不是问题。但是,现在我需要为不提供查询提供程序(IQueryProvider)的数据源实现IQueryAction<'T>模型。我现在可以重构我的接口来使用IEnumerable<'T>而不是IQueryable<'T>,并期望Func 委托用于键选择器而不是lambda表达式

现在我的问题是:这是否会导致排序操作在内存中运行,而不是在查询提供程序上,当IQueryable<'T>被简单地转换为IEnumerable<'T> ?既然我不再传递表达式,而是传递委托,那么查询提供程序如何仍然知道我想为查询使用的 ?如果这将不再工作:我能做些什么来适应两者,在内存和查询提供程序上排序,取决于IEnumerable<'T>的底层类型?

用法示例
public class MyEntity {
    public int    Id   { get; set; }
    public string Name { get; set }
}
// somewhere in code
var orderByName = new SortingAction<MyEntity, string>(x => x.Name);
myEntityRepository.GetAll(orderByName);
// ef repository impl
public IEnumerable<TEntity> GetAll(IQueryAction<TEntity> queryAction) {
    return queryAction.ApplyTo(dbContext.Set<TEntity>()); // runs on the query provider
}
// misc repository impl not supporting IQueryable/IQueryProvider
public IEnumerable<TEntity> GetAll(IQueryAction<TEntity> queryAction) {
    var result = someProvider.Sql("SELECT *")...
    return queryAction.ApplyTo(result);
}

Applying IEnumerable<'T>.OrderBy on a IQueryable&l

我能做些什么来适应这两个,在内存和查询上排序供应商

保持IQueryAction<TEntity>不变,并将IEnumerable<T>转换为IQueryable<T>,使用AsQueryable扩展:

var list = new List<YourEntity> { /* ... */ };
var queryableList = list.AsQueryable();
sortingAction.ApplyTo(queryableList);

最终,这将在内存中对内存序列(如集合)进行内存排序,并且它将向数据库发送查询,以获取"true"可查询项。

当IQueryable<'T>被简单地转换为IEnumerable<'T>时,这会导致排序操作在内存中运行而不是在查询提供程序上运行吗?

是的,因为您将绑定到在内存中进行排序的Enumerable扩展方法。在这一点上,底层提供者是什么并不重要;Enumerable扩展方法将枚举底层集合并在内存中对其"排序"。

我能做些什么来适应两者,在内存和查询提供程序上排序,取决于IEnumerable<T>的底层类型?

客户端可以在调用方法之前调用ToQueryable()。如果底层集合已经是IQueryable<T>,那么输入只是强制转换;否则,在IEnumerable之上放置一个适配器,使IQueryable方法适应IEnumerable方法