Реализация оболочки IQueryable для перевода объектов результатов
Обновление 2013-08-22:
Посмотрев наПостроение серии IQueryable провайдера (спасибо за ссылку!) Я получил немного дальше. Я обновил код соответственно. Это все еще не полностью работает все же. Если я правильно понимаю учебник, тоGetEnumerator
вызывается в том случае, если запрашивается несколько элементов (например,ToList()
вызов запрашиваемой или любой функции агрегации). Так что всеGetEnumerator
реализация обертки должна сделать, это вызватьExecute
на провайдера и передать запрашиваемуюс выражением В другом случае, если запрашивается только один элемент,Execute
называется напрямую. ЗапрашиваемаяВыражение s также отражает, относится ли оно к одному или нескольким элементам. Это правильно?
К сожалению, теперь я получаю InvalidOperationException, сказав:Последовательность содержит более одного элемента при звонкеExecute
на провайдере исходного запроса. Что это значит? Я просто передаю выражение без перевода, так как задействованы те же типы, что и упомянутые выше. Бит перевода сIEnumerable
в коде, вероятно, неполный, но сейчас я нея даже не дошел до этого.
Я пытаюсь реализовать простую оболочку IQueryable, используя один базовый IQueryable в качестве источника данных, который вызывает функцию перевода для каждого объекта результата.
Я подумал, что это будет относительно тривиально, поскольку единственное, что нужно сделать обертке, это переводить. Однако я не могне заставить мою реализацию работать.
Смотрите ниже, что я получил до сих пор. Для некоторых запросов это работает, но я получаюStackOverflowException InvalidOperationException в некоторый момент.Я предполагаю, что это происходит из-за циклической связи между моей запрашиваемой и моим поставщиком запросов. Но я не понимаю, как правильно это реализовать.
Вот мои вопросы и мысли по этому поводу:
1. Почему у IQueryable есть поставщик, который в свою очередь снова возвращает IQueryable? Безразлично»Т этот призыв к бесконечной рекурсии?
2. Почему недостаточно реализовать IEnumerator? Почему FirstOrDefault, например, не использует перечислитель для получения элемента? Когда я отлаживал, приложение GetEnumerator () не вызывалось FirstOrDefault () в моем запросе.
3. Поскольку перечислитель используется не во всех случаях, где находится правильная точка для вызова функции перевода? Методы Execute QueryProvider, казалось, были в правильном месте. Но нужно ли мне в некоторых случаях вызывать перевод в Enumerator? Обновить: Я знаю, понял, что мне нужно предоставить свой собственныйIEnumerable
реализация, обеспечивающаяTranslatingEnumerator
и верни это перечисление из моегоExecute
метод. Для того, чтобы получить счетчикGetEnumerator
звонкиExecute
(увидеть ниже). Похоже, что LINQ-код, запрашивающий перечислитель, гарантирует, что выражение на самом деле возвращает.IEnumerable
Некоторые примечания к коду:
Тип источника перевода называетсяTDatabaseEntity, целевой тип перевода называетсяTBusinessEntity.I»
по сути, предоставляет IQueryable, который берет объекты результата, извлеченные из базового IQueryable, и транслирует их в TBusinessEntityтип objects.I»
Я знаю, что выражение также необходимо перевести. Однако я отложил это в сторону, так как в моем действительном заявлении яиспользуя те же типы для TBusinessEntity и TDatabaseEntity, так что выражение может быть передано напрямую.
Объекты результата все еще должны быть переведены в другие экземпляры, несмотря на то, что они того же типа.Обновить: Мой уровень перевода уже работает в моем приложении, а также заботится о связанных объектах. Это'простореализация IQueryable оболочки вещь, которую яЯ застрял с. Я
боюсь, что вся реализация неверна - TODO в коде - это только мои собственные заметки.
Фон: яЭто своего рода реализация собственного отсоединения сущностей, полученных от DbContext, на моем уровне доступа к данным, чтобы не допустить соприкосновения моего бизнес-уровня с реальными сущностями - из-за некоторых ошибок в EF и других требований, которые я могу 't напрямую использовать отдельные объекты EF.
Спасибо за вашу помощь!
IQueryable реализацияinternal class TranslatingQueryable : IQueryable
{
private readonly IQueryProvider _provider;
private readonly IQueryable _source;
internal TranslatingQueryable(TranslatingQueryProvider provider, IQueryable source)
{
Guard.ThrowIfArgumentNull(provider, "provider");
Guard.ThrowIfArgumentNull(source, "source");
_provider = provider;
_source = source;
}
internal TranslatingQueryable(Func translateFunc, IQueryable databaseQueryable)
: this(new TranslatingQueryProvider(translateFunc, databaseQueryable.Provider), databaseQueryable)
{
}
public IEnumerator GetEnumerator()
{
return ((IEnumerable)Provider.Execute(Expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)Provider.Execute(Expression)).GetEnumerator();
}
public Expression Expression
{
get
{
return _source.Expression;
}
}
public Type ElementType
{
get
{
return typeof(TBusinessEntity);
}
}
public IQueryProvider Provider
{
get
{
return _provider;
}
}
}
Реализация IQueryProviderpublic class TranslatingQueryProvider : IQueryProvider
{
private readonly Func _translateFunc;
private readonly IQueryProvider _databaseQueryProvider;
public TranslatingQueryProvider(Func translateFunc, IQueryProvider databaseQueryProvider)
{
_translateFunc = translateFunc;
_databaseQueryProvider = databaseQueryProvider;
}
public IQueryable CreateQuery(Expression expression)
{
var databaseQueryable = _databaseQueryProvider.CreateQuery(expression);
return new TranslatingQueryable(this, databaseQueryable);
}
public IQueryable CreateQuery(Expression expression)
{
var databaseQueryable = _databaseQueryProvider.CreateQuery(expression);
return new TranslatingQueryable(this, databaseQueryable);
}
public object Execute(Expression expression)
{
return Execute(expression);
}
public TResult Execute(Expression expression)
{
// TODO This call throws an InvalidOperationException if an enumeration is requested
var databaseResult = _databaseQueryProvider.Execute(expression);
var databaseEnumerable = databaseResult as IEnumerable;
if (databaseEnumerable != null)
{
if (typeof(TResult).IsAssignableFrom(typeof(IEnumerable)))
{
throw new InvalidOperationException();
}
return (TResult)(object)new TranslatingEnumerable(databaseEnumerable, _translateFunc);
}
else
{
return (TResult)_translateFunc(databaseResult);
}
}
private class TranslatingEnumerable : IEnumerable
{
private readonly TranslatingEnumerator _enumerator;
public TranslatingEnumerable(IEnumerable databaseEnumerable, Func translateFunc)
{
_enumerator = new TranslatingEnumerator(translateFunc, databaseEnumerable.GetEnumerator());
}
public IEnumerator GetEnumerator()
{
return _enumerator;
}
}
}
Реализация IEnumeratorinternal class TranslatingEnumerator : IEnumerator
{
private readonly Func _translateFunc;
private readonly IEnumerator _databaseEnumerator;
internal TranslatingEnumerator(Func translateFunc, IEnumerator databaseEnumerator)
{
_translateFunc = translateFunc;
_databaseEnumerator = databaseEnumerator;
}
public bool MoveNext()
{
return _databaseEnumerator.MoveNext();
}
public void Reset()
{
_databaseEnumerator.Reset();
}
public object Current
{
get
{
return _translateFunc(_databaseEnumerator.Current);
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
}