Как кэшировать скомпилированный запрос LINQ (не результаты)?

Похоже, что каждый раз, когда я что-то запрашиваю с помощью LINQ, NHibernate строит этот запрос с нуля:

отчет dotTrace

Код выглядит так

session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();

Можно ли избежать перекомпиляции?

Также тот же вопрос о кэшировании запросов QueryOver/Criteria (не так критично, но все же может соответствовать области).


person Vlad    schedule 07.08.2017    source источник
comment
@stybl повторное использование запроса LINQ и повторное использование запроса LINQ к базе данных NH - это не одно и то же   -  person Vlad    schedule 08.08.2017
comment
Я в курсе, поэтому я отказался от закрытого голосования. Но забыл про комментарий.   -  person stybl    schedule 08.08.2017
comment
stackoverflow.com/a/4817010/1162077   -  person David Osborne    schedule 08.08.2017


Ответы (2)


Я напомню, что выражения с методом contains linq не компилируются, потому что перечисляемое может быть другим в будущих вызовах.

Возможный обходной путь - использовать уродливый способ оператора ИЛИ, если вы знаете, что количество элементов всегда одинаково.

Другой обходной путь — вызвать следующий метод:

session.QueryOver().AndRestrictionOn(x=>x.id).IsIn(ids)

person Molem    schedule 14.06.2020

Конкретно этот случай был вызван тем, что доступ к id (int[]) здесь

session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();

был преобразован в MemberAccessExpression (не ConstantExpression), и NHibernate должен был его оценить. Хотя ids никогда не менялся, он все равно был захвачен в класс, сгенерированный замыканием (например, DisplayClass<>.ids).

Я оптимизировал этот случай, сделав собственную версию PartialEvaluatingExpressionTreeVisitor:

    protected Expression EvaluateSubtree(Expression subtree)
    {
        ArgumentUtility.CheckNotNull(nameof(subtree), subtree);
        var memberExpression = subtree as MemberExpression;
        if (memberExpression != null)
        {
            Expression constant;
            if (TryEvaluateMember(memberExpression, out constant)) return constant;
        }

        if (subtree.NodeType != ExpressionType.Constant)
            throw new NHibernateExpressionOptimizerException(subtree);
        ConstantExpression constantExpression = (ConstantExpression)subtree;
        IQueryable queryable = constantExpression.Value as IQueryable;
        if (queryable != null && queryable.Expression != constantExpression)
            return queryable.Expression;
        return constantExpression;
    }

    bool TryEvaluateMember(MemberExpression memberExpression, out Expression constant)
    {
        constant = null;
        ConstantExpression c = memberExpression.Expression == null ? Expression.Constant(null) : EvaluateSubtree(memberExpression.Expression) as ConstantExpression;
        if (c == null) return false;
        var fieldInfo = memberExpression.Member as FieldInfo;
        if (fieldInfo != null)
        {
            constant = Expression.Constant(ReflectorReadFieldDelegate(fieldInfo, c.Value));
            return true;
        }

        var propertyInfo = memberExpression.Member as PropertyInfo;
        if (propertyInfo != null)
        {
            constant = Expression.Constant(ReflectorReadPropertyDelegate(propertyInfo, c.Value));
            return true;
        }
        return false;
    }

Делегаты рефлектора используют своего рода кешированную магию отражения.

person Vlad    schedule 08.08.2017
comment
Это похоже на изменение поведения по умолчанию, что мне в целом не нравится. Почему бы не обернуть его в вызов метода, например, public static T ToConst‹T›(T item) { return item;}, а затем применить свою логику для вызова этого метода? это дает вам больше гибкости и более ясно, каково ваше намерение, не так ли? - person MBoros; 12.08.2017