修改NHibernate查询的排序规则

时间:2013-09-09 10:45:36

标签: sql nhibernate collation

我有一个大型ASP.NET应用程序,它使用NHibernate(v3.2.0.4000)访问SQL Server 2005作为后端数据存储。我们有多个客户,每个客户都有自己的数据库,但所有数据库都运行在同一台服务器上。

快速提问:
如果没有在数据库服务器或各个数据库本身上设置COLLATION,或者重新编写现有的单个NHibernate查询代码,如何在NHibernate查询中添加自定义COLLATION语句,纯粹出于订购目的,在它们访问数据库之前?< /强>

问题较长:
由于拥有一些新的国际客户,我们需要支持自定义整理以便在我们的应用程序中进行分类。我们不能在数据库(或服务器)级别上执行此操作,因为我们为同一服务器上的每个客户托管一个单独的数据库(每个客户可能具有不同的排序规则要求),并且存在诸如TempDB等和多个不同的事情的复杂性同一服务器上的排序规则(请参阅herehere)。

由于NHibernate在整个代码库中的使用方式存在差异(即HQL,ICriteria,普通SQL,QueryOver和Linq的组合),并且由于其中许多查询非常复杂和混乱,我们希望完全避免改变任何现有的NHibernate查询代码。

SQL Server将允许调用在该查询的运行时应用特定排序规则的查询,如下所示:SELECT * FROM Customer ORDER BY Surname COLLATE Latin1_General_CI_AS

我也知道NHibernate允许创建Interceptor,可以在全局或特定于会话的基础上添加到NHibernate(如演示here),允许拦截SQL在将其发送到SQL Server之前的语句。

似乎我可以编写一个拦截器类来拦截SQL语句,但是,它只是允许我捕获原始SQL语句(完整的NHibernate的奇怪和美妙的字段和别名)。我不知道是否有任何方法可以干净地解析查询的特定部分(我只需要ORDER BY子句)并在检查字段后更改ORDER BY子句的各个组成部分(即各个字段)是基于文本的,并允许将COLLATE语句附加到它们。

NHibernate公开了一个NHibernate.SqlCommand.ISqlStringVisitor接口,听起来很有前途,似乎对SqlStringOnPrepareStatement类中可以覆盖的EmptyInterceptor方法中捕获的{{1}}进行操作,但是,我对NHibernate的这一部分完全不熟悉,并且目前没有任何帮助,正如我写的那样,NHibernate所谓的确切信息来源nhibernate.info已经失效(似乎已经持续了好几周)! / p>

有没有人必须执行这样的任务?是否有可能以干净且类型安全的方式解构NHibernate查询?是否有一种完全不同的方法可以实现相同的目标(考虑到无法改变数据库/服务器整理的相同限制)?

1 个答案:

答案 0 :(得分:4)

使用自定义投影,您可以相对轻松地完成此操作。下课是一个很好的起点:

public class OrderByWithCollate : SimpleProjection
{
    public string ColumnName { get; private set; }
    public string Collation { get; private set; }

    public OrderByWithCollate(string columnName, string collation)
    {
        ColumnName = columnName;
        Collation = collation;
    }

    public override SqlString ToGroupSqlString(
        ICriteria criteria, 
        ICriteriaQuery criteriaQuery, 
        IDictionary<string, IFilter> enabledFilters)
    {
        throw new NotImplementedException();   
    }

    public override SqlString ToSqlString(
        ICriteria criteria, 
        int position, 
        ICriteriaQuery criteriaQuery, 
        IDictionary<string, IFilter> enabledFilters)
    {
        return new SqlStringBuilder()
            .Add(ColumnName)
            .Add(" COLLATE ").Add(Collation)
            .Add(" as __collate_").ToSqlString();
    }

    public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
    {
        return new IType[] {NHibernateUtil.String};
    }

    public override bool IsGrouped { get { return false; } }

    public override bool IsAggregate { get { return false; } }
}

但是,这种方法显然只适用于CriteriaAPI和QueryOver:

session.QueryOver<Item>()
    .OrderBy(new OrderByWithCollate("Name", "Latin1_General_CI_AS")).Asc
    .List();

该查询中的SQL类似于:

SELECT * FROM Item ORDER BY Name COLLATE Latin1_General_CI_AS asc

我不太确定HQL或LINQ,因为我很少使用它们,尤其是LINQ,我从未使用过它,从第一天起就认为它是一种有毒资产,并明确阻止我的队友使用它。

当然,这种方法需要更新现有的查询,但我认为应用并不是非常耗时,因为这是标准NHibernate排序的替代品,因此通过搜索和替换,您可以替换所有出现的排序。您可以通过检查提供的条件和属性名称来安排复杂的Collat​​ionProjection,它知道何时应用排序规则。

为了实现自定义投影,阅读NHibernate源代码是获取知识的好方法,因为几乎没有文档涉及这个主题。

我不认为拦截IInterceptor.OnPrepareStatement是一个不错的选择,但如果你想继续这个方向,那么你可以尝试从原始SQL字符串的部分重建SQL字符串,嗅探ORDER BY子句处理。我想没有人做过,所以你进行了大量的实验,会有很多奇怪的问题让你感到惊讶。