NHibernate,通过扩展 DefaultLinqToHqlGeneratorsRegistry 使用 SQL 方法扩展 IQueryable

NHibernate, extend IQueryable with SQL method by extending DefaultLinqToHqlGeneratorsRegistry

我使用流畅的 NHibernate,我需要随机化查询结果,我想要的是这样的:

select * from table order by newid()

方法,应该扩展 NHibernate IQueryable 生成器以使用类似 QueryableExtension.RandomOrder<T>(this IQueryable<T> list)

的方法

通过此处的博客:http://fabiomaulo.blogspot.dk/2010/07/nhibernate-linq-provider-extension.html 还有这个:Extending LINQ to Nhibernate provider, in combination with Dynamic LINQ problem

我写了这段代码:

public class RandomOrderGenerator : BaseHqlGeneratorForMethod
{
    public RandomOrderGenerator()
    {
        SupportedMethods = new[]
        {
            ReflectionHelper.GetMethod(() => Enumerable.Empty<object>().AsQueryable().RandomOrder()),
            ReflectionHelper.GetMethod(() => Enumerable.Empty<long>().AsQueryable().RandomOrder()),
        };
    }

    public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
        HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        throw new NotImplementedException();
    }
}

public class MyLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
    public MyLinqToHqlGeneratorsRegistry()
    {
        RegisterGenerator(ReflectionHelper.GetMethod(() => Enumerable.Empty<object>().AsQueryable().RandomOrder()), new RandomOrderGenerator());
    }
}

我已配置使用 MyLinqToHqlGeneratorsRegistry,它已创建,我的 RandomOrderGenerator 已创建,但从未调用 BuildHql 方法。

扩展名的使用:

repository.Query<Table>().Take(10).RandomOrder().Select(x => x.Id);

SupportMethodsRegisterGenerator的方法定义应该是一样的,但是为什么我获取不到生成HQL?

现在有了解决方案。

我无法使 RandomOrder 扩展正常工作,但后来我读到了: https://nhibernate.jira.com/browse/NH-3386

这里有 LinqExtensionMethod,它会自动调用 sql 中的方法。这里的问题是当表达式不依赖于数据库中的任何特定内容时,它会在本地编译,为此要传递特定于该方法的数据库。

repository.Query<Table>().Take(10).OrderBy(x => OrderType.Random(x.Id))...

LinqExtensionMethods 由 DefaultLinqToHqlGeneratorsRegistry 处理,并将参数映射到 SQL 方法,非常简洁,但不是我们想要的。现在 sql 看起来像这样: select....newid(table.id)

为了解决这个问题,我们需要自己映射它,这样我们就可以忽略参数,所以不用 Attribute 并创建一个 hql 生成器:

public class RandomOrderHqlGenerator : BaseHqlGeneratorForMethod
{
    private readonly string _name;
    public RandomOrderHqlGenerator()
    {
        _name = "NewId";
    }
    public override HqlTreeNode BuildHql(MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
        HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        return treeBuilder.MethodCall(_name);
    }
}

RegisterGenerator(ReflectionHelper.GetMethodDefinition(() => OrderType.Random(null)), new RandomOrderHqlGenerator());