我已经用谷歌搜索并提出了示例代码,但这给我带来了麻烦。 根据我的发现,这是我得到的:
在我有的持久类中
public static readonly Expression<Func<Detail, decimal>> TotExpression = d =>
(decimal)((d.Fee == null ? 0 : d.Fee) + (d.Expenses == null ? 0 : d.Expenses));
public static Func<Detail, decimal> CompiledTot => TotExpression.Compile();
public virtual decimal Tot => CompiledTot(this);
我使用
class ComputedPropertyGeneratorRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public ComputedPropertyGeneratorRegistry()
{
CalculatedPropertyGenerator<Detail, decimal>.Register(
this,
x => x.Tot,
Detail.TotExpression);
}
}
public class CalculatedPropertyGenerator<T, TResult> : BaseHqlGeneratorForProperty
{
public static void Register(ILinqToHqlGeneratorsRegistry registry, Expression<Func<T, TResult>> property, Expression<Func<T, TResult>> calculationExp)
{
registry.RegisterGenerator(ReflectHelper.GetProperty(property), new CalculatedPropertyGenerator<T, TResult> { _calculationExp = calculationExp });
}
private CalculatedPropertyGenerator() { } // Private constructor
private Expression<Func<T, TResult>> _calculationExp;
public override HqlTreeNode BuildHql(MemberInfo member, Expression expression, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return visitor.Visit(_calculationExp);
}
}
在我的会话工厂配置中,我有
cfg.LinqToHqlGeneratorsRegistry<ComputedPropertyGeneratorRegistry>();
然而当我跑步时
session.Query<Detail>().Select(x => x.Tot).First();
我得到
NHibernate.Hql.Ast.ANTLR.InvalidPathException: Invalid path: 'd.Fee'
似乎当NH尝试生成SQL时,它会在某个时候LiteralProcessor.LookupConstant
d.Fee
,调用ReflectHelper.GetConstantValue("d.Fee")
,由于某种原因,它假定"d"是属性所属类的名称。 当然不是,这打破了一切。 我不知道为什么它会走上这条错误的道路。
好的,问题似乎是 HQL 生成器返回一个表达式,其中"d"参数不被视为参数,因此生成的 HQL 不知道如何处理它。 如果我将查询中的"x"参数更改为"d",如
session.Query<Detail>().Select(d => d.Tot).First();
这一切都悬而未决。 这显然是一个麻烦,但不足以超过能够搜索和选择计算属性。 我认为更了解HQL"访客"的人能够在HQL生成器中进行适当的调整,但我会把它留给其他志愿者。
更新:我不能把它留在那里,所以我在Phil Klein的一些代码的帮助下拼凑出一种方法来做到这一点。
菲尔提供了这个课程
public class PredicateRewriter
{
public static Expression<Func<T, TResult>> Rewrite<T, TResult>(Expression<Func<T, TResult>> exp, string newParamName)
{
var param = Expression.Parameter(exp.Parameters[0].Type, newParamName);
var newExpression = new PredicateRewriterVisitor(param).Visit(exp);
return (Expression<Func<T, TResult>>)newExpression;
}
private class PredicateRewriterVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameterExpression;
public PredicateRewriterVisitor(ParameterExpression parameterExpression)
{
_parameterExpression = parameterExpression;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return _parameterExpression;
}
}
}
我在这里使用
public override HqlTreeNode BuildHql(MemberInfo member, Expression expression, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
// this is a kludge because I don't know how to pry the name out of the parameter expression
var inside = new Regex("\[(.*)\]");
var name = inside.Match(expression.ToString()).Groups[1].Value;
return visitor.Visit(PredicateRewriter.Rewrite<T, TResult>(_calculationExp, name));
}
如果表达式有多个参数,这是行不通的,但这种情况很少发生,我真的不想以完善这个:)为职业。
更新:我有一个解决方案。 我没有子类化DefaultLinqToHqlGeneratorsRegistry
,这似乎在将LINQ转换为SQL的链条上太靠后,我DefaultQueryProvider, IQueryProviderWithOptions
子类化并用cfg.LinqQueryProvider<CustomQueryProvider>()
插入它。
这是基于我在这里找到的Ivan Stoev的代码。