Vlad
Vlad

Reputation: 3171

How do I cache compiled LINQ query (not results)?

It looks like each time I query something with LINQ NHibernate builds that query from scratch:

dotTrace report

The code looks like

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

Is it possible to avoid recompiling it?

Also the same question about caching QueryOver/Criteria queries (not so critical but it may still fit the scope).

Upvotes: 2

Views: 216

Answers (2)

Molem
Molem

Reputation: 119

I recall that expressions with the contains linq method are not compiled because the enumerable could be different in future calls

A possible workaround is to use the ugly way of OR operator if you know that the number of elements is always the same

Another workaround is to call the following method:

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

Upvotes: 1

Vlad
Vlad

Reputation: 3171

Particularly this case was caused by that the access to ids (int[]) here

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

was transformed into MemberAccessExpression (not ConstantExpression) and NHibernate had to evaluate it. Though ids was never changed it still was captured into a closure generated class (like DisplayClass<>.ids).

I optimized this case by making my own version of 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;
    }

The reflector delegates use a kind of cached reflection.emit magic.

Upvotes: 0

Related Questions