在IQueryable<'T>上应用IEnumerable<'T> .OrderBy

时间:2015-10-01 12:30:39

标签: c# entity-framework linq expression ienumerable

我有一个自定义类型,我在我的存储库中使用它可以帮助我明确表示查询选项,例如用于排序,分页等。

最初,界面看起来像这样:

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,所以这不是问题。但是现在我需要为不提供查询提供程序(IQueryAction<'T>)的数据源实现IQueryProvider模型。我现在可以重构我的界面以使用IEnumerable<'T>而不是IQueryable<'T>,并期望Func 委托用于键选择器而不是 lambda表达式

现在我的问题是:当IQueryable<'T>被简单地转换为IEnumerable<'T>时,这是否会导致排序操作在内存中而不是在查询提供程序上运行?由于我不再传递表达式,而是传递委托,查询提供程序如何仍然知道我想要的 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);
}

2 个答案:

答案 0 :(得分:1)

  

我可以做些什么,在内存和查询中排序   提供商

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

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

sortingAction.ApplyTo(queryableList);

最终,这将对内存中的序列(如集合)进行内存中排序,并且它将针对&#34; true&#34;的数据库发送查询。 queryables。

答案 1 :(得分:0)

  

当IQueryable&lt;&gt; T&gt;时,这会导致排序操作在内存中而不是在查询提供程序上运行。只是被转换为IEnumerable&lt;&quot; T&gt;?

是的,因为您将绑定到在内存中进行排序的Enumerable扩展方法。在那一点上,底层提供者是什么并不重要; Enumerable扩展方法将枚举基础集合和&#34;排序&#34;它在内存中。

  

根据IEnumerable<T>的基础类型,我可以做些什么来适应内存和查询提供程序的排序?

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