Реализация оболочки IQueryable для перевода объектов результатов

Обновление 2013-08-22:

Посмотрев «Построение серии провайдеров IQueryable» (спасибо за ссылку!), Я немного продвинулся. Я обновил код соответственно. Это все еще не полностью работает все же. Если я правильно понимаю учебник, тоGetEnumerator вызывается в том случае, если запрашивается несколько элементов (например,ToList() вызов запрашиваемой или любой функции агрегации). Так что всеGetEnumerator реализация обертки должна сделать, это вызватьExecute на провайдере и передайте выражение запрашиваемого. В другом случае, если запрашивается только один элемент,Execute называется напрямую. Выражение запроса также отражает, относится ли оно к одному или нескольким элементам. Это верно?

К сожалению, теперь я получаю 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.

По сути, я предоставляю IQueryable, который берет объекты результатов, извлеченные из базового IQueryable, и переводит их в TBusinessEntity.тип объекты.

Я знаю, что выражение также необходимо перевести. Однако я отложил это в сторону, поскольку в моем реальном приложении я использую те же типы для TBusinessEntity и TDatabaseEntity, поэтому выражение может быть передано напрямую.

Объекты результата все еще должны быть переведены в другие экземпляры, несмотря на то, что они того же типа.Обновить: Мой уровень перевода уже работает в моем приложении, а также заботится о связанных объектах. Это просто «реализация оболочки IQueryable», с которой я застрял.

Боюсь, что вся реализация неверна - TODO в коде - это только мои собственные заметки.

Фон: Я как бы реализую свое собственное отключение сущностей, полученных от DbContext, в моем уровне доступа к данным, чтобы не допустить соприкосновения моего бизнес-уровня с реальными сущностями - из-за некоторых ошибок в EF и других требований я не могу напрямую использовать EF отдельные объекты.

Спасибо за вашу помощь!

IQueryable реализация
internal class TranslatingQueryable<TDatabaseEntity, TBusinessEntity> : IQueryable<TBusinessEntity>
{
    private readonly IQueryProvider _provider;
    private readonly IQueryable<TDatabaseEntity> _source;

    internal TranslatingQueryable(TranslatingQueryProvider provider, IQueryable<TDatabaseEntity> source)
    {
        Guard.ThrowIfArgumentNull(provider, "provider");
        Guard.ThrowIfArgumentNull(source, "source");

        _provider = provider;
        _source = source;
    }

    internal TranslatingQueryable(Func<object, object> translateFunc, IQueryable<TDatabaseEntity> databaseQueryable)
        : this(new TranslatingQueryProvider(translateFunc, databaseQueryable.Provider), databaseQueryable)
    {
    }

    public IEnumerator<TBusinessEntity> GetEnumerator()
    {
        return ((IEnumerable<TBusinessEntity>)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;
        }
    }
}
Реализация IQueryProvider
public class TranslatingQueryProvider : IQueryProvider
{
    private readonly Func<object, object> _translateFunc;
    private readonly IQueryProvider _databaseQueryProvider;

    public TranslatingQueryProvider(Func<object, object> translateFunc, IQueryProvider databaseQueryProvider)
    {
        _translateFunc = translateFunc;
        _databaseQueryProvider = databaseQueryProvider;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        var databaseQueryable = _databaseQueryProvider.CreateQuery<object>(expression);

        return new TranslatingQueryable<object, object>(this, databaseQueryable);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        var databaseQueryable = _databaseQueryProvider.CreateQuery<object>(expression);

        return new TranslatingQueryable<object, TElement>(this, databaseQueryable);
    }

    public object Execute(Expression expression)
    {
        return Execute<object>(expression);
    }

    public TResult Execute<TResult>(Expression expression)
    {
        // TODO This call throws an InvalidOperationException if an enumeration is requested
        var databaseResult = _databaseQueryProvider.Execute<TResult>(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<object, object> translateFunc)
        {
            _enumerator = new TranslatingEnumerator(translateFunc, databaseEnumerable.GetEnumerator());
        }

        public IEnumerator GetEnumerator()
        {
            return _enumerator;
        }
    }
}
Реализация IEnumerator
internal class TranslatingEnumerator : IEnumerator
{
    private readonly Func<object, object> _translateFunc;
    private readonly IEnumerator _databaseEnumerator;

    internal TranslatingEnumerator(Func<object, object> 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;
        }
    }
}

Ответы на вопрос(2)

Ваш ответ на вопрос