在 IQueryable<'T> 上应用 IEnumerable<'T>.OrderBy
Applying IEnumerable<'T>.OrderBy on a IQueryable<'T>
我有一个自定义类型,我在我的存储库中使用它来帮助我明确表示查询选项,例如用于排序、分页等
最初,界面是这样的:
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);
}
}
大部分时间我都使用 Entity Framework,所以这不是问题。但是现在我需要为不提供查询提供程序 (IQueryProvider
) 的数据源实现 IQueryAction<'T>
模型。我现在可以重构我的界面以使用 IEnumerable<'T>
而不是 IQueryable<'T>
并期望 Func
delegate 作为键选择器而不是 lambda 表达式。
现在我的问题是:当 IQueryable<'T>
只是被转换为 IEnumerable<'T>
时,这是否会导致排序操作在内存中而不是在查询提供程序中 运行?由于我不再传递 expression,而是传递 delegate,查询提供程序怎么可能仍然知道 key 我想用它作查询?如果这不再有效:根据 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);
}
Would this cause the sort action to be run in memory instead of on the query provider when an IQueryable<'T> is simply being casted to an IEnumerable<'T>?
是的,因为您将绑定到在内存中进行排序的 Enumerable
扩展方法。那时底层提供者是什么并不重要; Enumerable
扩展方法将枚举底层集合并 "sort" 它在内存中。
What could I do to fit both, sorting in memory and on the query provider, depending on the underlying type of IEnumerable<T>
?
客户端可以在调用方法前调用ToQueryable()
。如果基础集合是 already 和 IQueryable<T>
那么输入只是被转换;否则,将适配器放置在 IEnumerable
之上,以将 IQueryable
方法适配为 IEnumerable
方法
What could I do to fit both, sorting in memory and on the query
provider
保持 IQueryAction<TEntity>
不变,并将 IEnumerable<T>
转换为 IQueryable<T>
,使用 AsQueryable
扩展名:
var list = new List<YourEntity> { /* ... */ };
var queryableList = list.AsQueryable();
sortingAction.ApplyTo(queryableList);
最终这将对内存序列(如集合)进行内存排序,并将针对 "true" 个可查询对象向数据库发送查询。
我有一个自定义类型,我在我的存储库中使用它来帮助我明确表示查询选项,例如用于排序、分页等
最初,界面是这样的:
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);
}
}
大部分时间我都使用 Entity Framework,所以这不是问题。但是现在我需要为不提供查询提供程序 (IQueryProvider
) 的数据源实现 IQueryAction<'T>
模型。我现在可以重构我的界面以使用 IEnumerable<'T>
而不是 IQueryable<'T>
并期望 Func
delegate 作为键选择器而不是 lambda 表达式。
现在我的问题是:当 IQueryable<'T>
只是被转换为 IEnumerable<'T>
时,这是否会导致排序操作在内存中而不是在查询提供程序中 运行?由于我不再传递 expression,而是传递 delegate,查询提供程序怎么可能仍然知道 key 我想用它作查询?如果这不再有效:根据 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);
}
Would this cause the sort action to be run in memory instead of on the query provider when an IQueryable<'T> is simply being casted to an IEnumerable<'T>?
是的,因为您将绑定到在内存中进行排序的 Enumerable
扩展方法。那时底层提供者是什么并不重要; Enumerable
扩展方法将枚举底层集合并 "sort" 它在内存中。
What could I do to fit both, sorting in memory and on the query provider, depending on the underlying type of
IEnumerable<T>
?
客户端可以在调用方法前调用ToQueryable()
。如果基础集合是 already 和 IQueryable<T>
那么输入只是被转换;否则,将适配器放置在 IEnumerable
之上,以将 IQueryable
方法适配为 IEnumerable
方法
What could I do to fit both, sorting in memory and on the query provider
保持 IQueryAction<TEntity>
不变,并将 IEnumerable<T>
转换为 IQueryable<T>
,使用 AsQueryable
扩展名:
var list = new List<YourEntity> { /* ... */ };
var queryableList = list.AsQueryable();
sortingAction.ApplyTo(queryableList);
最终这将对内存序列(如集合)进行内存排序,并将针对 "true" 个可查询对象向数据库发送查询。