Таким образом, у вас есть IQueryable<T>
, которое будет выполнено для DbContext A, как только запрос будет выполнен, и вы хотите, чтобы тот же запрос выполнялся на DbContext B, когда запрос выполняется.
Для этого вам нужно понять разницу между IEnumerable<T>
и IQueryable<T>
.
IEnumerable<T>
содержит весь код для перечисления элементов, представляемых перечисляемым. Перечисление начинается при вызове GetEnumerator
и MoveNext
. Это можно сделать явно. Однако это обычно делается неявно такими функциями, как foreach
, ToList
, FirstOrDefault
и т. Д.
IQueryable
не содержит код для перечисления, он содержит Expression
и Provider
. Поставщик знает, кто будет выполнять запрос, и знает, как перевести Expression
на язык, понятный исполнителю запроса.
Благодаря такому разделению можно разрешить выполнение одного и того же выражения разными источниками данных. Они даже не должны относиться к одному и тому же типу: один источник данных может быть системой управления базой данных, которая понимает SQL, а другой может быть файлом, разделенным запятыми.
Пока вы объединяете операторы Linq, которые возвращают IQueryable, запрос не выполняется, изменяется только выражение.
Как только начинается перечисление, либо вызывая GetEnumerator / MoveNext, либо используя foreach или одну из функций LINQ, которые не возвращают IQueryable, поставщик будет переводить выражение на язык, который понимает источник данных и взаимодействует с ним источник данных для выполнения запроса. Результатом запроса является IEnumerable, который можно перечислять, как если бы все данные были в локальном коде.
Некоторые провайдеры умны и используют некоторую буферизацию, так что не все данные переносятся в локальную память, а только часть данных. Новые данные запрашиваются при необходимости. Поэтому, если вы выполняете foreach в базе данных с миллиардом элементов, запрашиваются только первые несколько (тысячи) элементов. Запрашиваются дополнительные данные, если в вашем foreach заканчиваются извлеченные данные.
Итак, у вас уже есть один IQueryable<T>
, поэтому у вас есть Expression
a Provider
и ElementType. Вы хотите, чтобы тот же Expression / ElementType to be executed by a different
Провайдер . You even want to change the
Выражение` немного перед его выполнением.
Следовательно, вам нужно иметь возможность создать объект, который реализует 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();