如何缓存已编译的 LINQ 查询(不是结果)?
How do I cache compiled LINQ query (not results)?
看起来每次我使用 LINQ NHibernate 查询内容时都会从头开始构建该查询:
代码看起来像
session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();
是否可以避免重新编译?
还有关于缓存 QueryOver/Criteria 查询的相同问题(不是很重要,但它可能仍然适合范围)。
特别是这种情况是由于访问 ids (int[]
) 这里
session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();
被转换为 MemberAccessExpression
(不是 ConstantExpression
),NHibernate 必须对其求值。尽管 ids
从未改变,但它仍然被捕获到生成的闭包中 class (如 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;
}
反射器委托使用一种缓存的reflection.emit魔法。
我记得带有 contains linq 方法的表达式不会被编译,因为在以后的调用中枚举可能会有所不同
如果您知道元素的数量始终相同,则可能的解决方法是使用丑陋的 OR 运算符方式
另一种解决方法是调用以下方法:
session.QueryOver().AndRestrictionOn(x=>x.id).IsIn(ids)
看起来每次我使用 LINQ NHibernate 查询内容时都会从头开始构建该查询:
代码看起来像
session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();
是否可以避免重新编译?
还有关于缓存 QueryOver/Criteria 查询的相同问题(不是很重要,但它可能仍然适合范围)。
特别是这种情况是由于访问 ids (int[]
) 这里
session.Query<User>().Where(x => ids.Contains(x.Id)).ToFuture();
被转换为 MemberAccessExpression
(不是 ConstantExpression
),NHibernate 必须对其求值。尽管 ids
从未改变,但它仍然被捕获到生成的闭包中 class (如 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;
}
反射器委托使用一种缓存的reflection.emit魔法。
我记得带有 contains linq 方法的表达式不会被编译,因为在以后的调用中枚举可能会有所不同
如果您知道元素的数量始终相同,则可能的解决方法是使用丑陋的 OR 运算符方式
另一种解决方法是调用以下方法:
session.QueryOver().AndRestrictionOn(x=>x.id).IsIn(ids)