简体   繁体   English

将IEnumerable <'T> .OrderBy应用于IQueryable <'T>

[英]Applying IEnumerable<'T>.OrderBy on a IQueryable<'T>

I've got a custom type which I use on my repositories that helps me explicitly represent query options, eg for sorting, paging etc. 我在存储库中使用了一个自定义类型,可以帮助我显式表示查询选项,例如排序,分页等。

Initially, the interface looked like this: 最初,界面如下所示:

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

With that I can represent sorting like this: 这样我就可以表示这样的排序:

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);
    }

}

Most of the time I work with Entity Framework, so this is not a problem. 大多数时候,我使用实体框架,所以这不是问题。 However now I need to implement the IQueryAction<'T> model for a data source which doesn't provide a query provider ( IQueryProvider ). 但是,现在我需要为不提供查询提供程序( IQueryProvider )的数据源实现IQueryAction<'T>模型。 I could now refactor my interface to use IEnumerable<'T> instead of IQueryable<'T> and expect a Func delegate for the key selector instead of a lambda expression . 现在,我可以重构接口以使用IEnumerable<'T>而不是IQueryable<'T>并期望键选择器使用Func 委托代替lambda表达式

Now my question is: 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> ? 现在我的问题是:当将IQueryable<'T>仅仅转换为IEnumerable<'T>时,这是否会导致排序操作在内存中而不是在查询提供程序上运行? Since I wouldn't pass an expression anymore, but rather a delegate , how could the query provider still know what key I'd like to use for the query? 因为我不会通过一个表情了,而是一个委托 ,怎么能查询供应商还知道想什么我使用的查询? And if that won't work anymore: What could I do to fit both, sorting in memory and on the query provider, depending on the underlying type of IEnumerable<'T> ? 并且,如果该方法不再起作用:根据IEnumerable<'T>的基础类型,我该怎么做才能同时适合内存和查询提供程序中的排序?

Usage example 使用范例

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);
}

What could I do to fit both, sorting in memory and on the query provider 我要怎么做才能同时适合内存和查询提供程序中的排序

Leave IQueryAction<TEntity> as is, and convert IEnumerable<T> to IQueryable<T> , using AsQueryable extension: 保持IQueryAction<TEntity> ,并使用AsQueryable扩展将IEnumerable<T>转换为IQueryable<T>

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

sortingAction.ApplyTo(queryableList);

Ultimately this will do in-memory sorting for in-memory sequences (like collections), and it will send queries against databases for "true" queryables. 最终,这将对内存中的序列(如集合)进行内存中排序,并且它将针对“真”可查询对象对数据库发送查询。

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>? 当将IQueryable <'T>简单地转换为IEnumerable <'T>时,这是否会使排序操作在内存中而不是在查询提供程序上运行?

Yes, because you will be binding to the Enumerable extension methods which do sorting in-memory. 是的,因为您将绑定到在内存中进行排序的Enumerable扩展方法。 It doesn't matter at that point what the underlying provider is; 那时底层的提供者是什么都没有关系。 the Enumerable extension methods will enumerate the underlying collection and "sort" it in-memory. Enumerable扩展方法将枚举基础集合并将其“分类”在内存中。

What could I do to fit both, sorting in memory and on the query provider, depending on the underlying type of IEnumerable<T> ? 根据IEnumerable<T>的基础类型,我该怎么做才能同时适合内存和查询提供程序?

The client can call ToQueryable() before calling the methods. 客户端可以在调用方法之前调用ToQueryable() If the underlying collection is already an IQueryable<T> then the input is just cast; 如果基础集合已经IQueryable<T>则仅对输入进行强制转换; otherwise an adapter is placed on top of the IEnumerable to adapt the IQueryable methods to IEnumerable methods 否则,将适配器放在IEnumerable顶部,以使IQueryable方法适应IEnumerable方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM