如何让 NHibernate 为计算属性生成 SQL? [英] How do I get NHibernate to generate SQL for a computed property?

查看:48
本文介绍了如何让 NHibernate 为计算属性生成 SQL?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在谷歌上搜索并想出了示例代码,但它给我带来了麻烦.根据我的发现,这是我得到的:

I've Googled around and come up with sample code, but it's giving me trouble. Here's what I've got, based on what I found:

在我有的持久类

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 时,在某些时候,d.Fee 上的 LiteralProcessor.LookupConstant 调用了 ReflectHelper.GetConstantValue(d.Fee"),出于某种原因假设d"是属性所属的类的名称.当然不是,这打破了一切.我不知道为什么要走这条错误的道路.

It seems that when NH tries to generate the SQL it calls, at some point, LiteralProcessor.LookupConstant on d.Fee, which calls ReflectHelper.GetConstantValue("d.Fee"), which for some reason assumes that "d" is the name of the class to which the property belongs. Of course it isn't, which breaks everything. I have no idea why it's going down this wrong path.

推荐答案

好吧,问题似乎是 HQL 生成器返回了一个表达式,其中 'd' 参数不被视为参数,因此生成的 HQL 不会不知道该怎么办.如果我将查询中的x"参数更改为d",如

Okay, the problem seems to be that the HQL generator returns an expression where the 'd' parameter isn't treated as a parameter, so the resultant HQL doesn't know what to do with it. If I change the 'x' parameter in my query to 'd', as in

session.Query<Detail>().Select(d => d.Tot).First();

这一切都挂在一起.这显然是一个麻烦,但不足以超过能够搜索和选择计算属性.我假设有人了解 HQL访客"最好能够在 HQL 生成器中进行适当的调整,但我会将其留给其他志愿者.

it all hangs together. This is obviously a bother, but not enough of one to outweigh being able to search on and select computed properties. I assume someone who understands the HQL "visitor" better would be able to make the proper adjustments in the HQL generator, but I'll leave that for some other volunteer.

更新:我不能就此放弃,所以我在 Phil Klein.

UPDATE: I couldn't leave it at that, so I cobbled together a way to do it, with the help of some code from Phil Klein.

Phil 提供了这门课

Phil's provided this class

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));
    }

如果表达式有多个参数,这将不起作用,但这种情况很少发生,我真的不希望以此为业:).

It's not going to work if the expression has multiple parameters, but that rarely happens and I'm really not looking to make a career of refining this :).

更新:我有一个解决方案.我没有对 DefaultLinqToHqlGeneratorsRegistry 进行子类化,这似乎在将 LINQ 转换为 SQL 的过程中走得太远了,而是将 DefaultQueryProvider, IQueryProviderWithOptions 子类化,并用 cfg.LinqQueryProvider< 插入它.CustomQueryProvider>().

UPDATE: I have a resolution. Rather than subclassing DefaultLinqToHqlGeneratorsRegistry, which seems to be too far down the chain of converting LINQ to SQL, I subclassed DefaultQueryProvider, IQueryProviderWithOptions and inserted it with cfg.LinqQueryProvider<CustomQueryProvider>().

这是基于我发现的 Ivan Stoev 的代码此处.

This is based on code from Ivan Stoev that I found here.

这篇关于如何让 NHibernate 为计算属性生成 SQL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆