因此,您有一个IQueryable<T>,一旦执行查询,就会在 DbContext A 上执行,并且您希望在执行查询时在 DbContext B 上运行相同的查询。
为此,您必须了解IEnumerable<T> 和IQueryable<T> 之间的区别。
IEnumerable<T> 包含所有代码以枚举可枚举表示的元素。枚举在调用 GetEnumerator 和 MoveNext 时开始。这可以明确地完成。然而,它通常由 foreach、ToList、FirstOrDefault 等函数隐式完成。
IQueryable 不包含要枚举的代码,它包含 Expression 和 Provider。 Provider 知道谁将执行查询,并且知道如何将Expression 翻译成查询执行者可以理解的语言。
由于这种分离,可以让不同的数据源执行相同的表达式。它们甚至不必属于同一类型:一个数据源可以是理解 SQL 的数据库管理系统,另一个可以是逗号分隔的文件。
只要将返回 IQueryable 的 Linq 语句连接起来,就不会执行查询,只会更改表达式。
一旦枚举开始,通过调用 GetEnumerator / MoveNext,或者通过使用 foreach 或不返回 IQueryable 的 LINQ 函数之一,提供程序将表达式转换为数据源可以理解并与之通信的语言执行查询的数据源。查询的结果是一个 IEnumerable,可以像所有数据都在本地代码中一样进行枚举。
有些 Provider 很聪明,会使用一些缓冲,所以不是所有的数据都被传输到本地内存,而只是部分数据。需要时查询新数据。因此,如果您在包含无数元素的数据库中执行 foreach,则只会查询前几个(数千个)元素。如果您的 foreach 用完获取的数据,则会查询更多数据。
所以你已经有一个IQueryable<T>,因此你有一个Expression、一个Provider和一个ElementType。在执行之前,您需要相同的 Expression / ElementType to be executed by a differentProvider. You even want to change theExpression`。
因此,您需要能够创建一个实现IQueryable<T> 的对象,并且您希望能够设置Expression、ElementType 和Provider
class MyQueryable<T> : IQueryable<T>
{
public type ElementType {get; set;}
public Expression Expression {get; set;}
public Provider Provider {get; set;}
}
IQueryable<T> queryOnDbContextA= dbCotextA ...
IQueryable<T> setInDbContextB = dbContextB.Set<T>();
IQueryable<T> queryOnDbContextB = new MyQueryable<T>()
{
ElementType = queryOnDbContextA.ElementType,
Expression = queryOnDbContextB.Expression,
Provider = setInDbContextB.Provider,
}
如果需要,您可以在执行之前在其他上下文中调整查询:
var getPageOnContextB = queryOnDbContextB
.Skip(...)
.Take(...);
这两个查询仍未执行。执行它们:
var countA = await queryOnContextA.CountAsync();
var fetchedPageContextB = await getPageOnContextB.ToListAsync();