4

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

快速的问题:
如果没有在数据库服务器上设置 COLLATION,或者单独的数据库本身,或者重写现有的单独的 NHibernate 查询代码,我如何在 NHibernate 查询中添加自定义 COLLATION 语句,纯粹为了排序目的,在它们到达数据库之前?

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

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

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

我也知道 NHibernate 允许创建一个Interceptor可以在全局或特定于会话的基础上添加到 NHibernate 的(如此处所示,允许在 SQL 语句发送到 SQL Server 之前拦截它。

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

NHibernate 公开了一个NHibernate.SqlCommand.ISqlStringVisitor听起来很有希望的接口,并且似乎可以对类的方法中SqlString捕获的内容进行操作,该方法可以被覆盖,但是,我对 NHibernate 的这一部分完全不熟悉,目前它并没有帮助,正如我所写这,NHibernate 所谓的权威信息来源nhibernate.info已经关闭(并且似乎已经关闭了数周)!OnPrepareStatementEmptyInterceptor

有没有人必须执行这样的任务?是否可以以干净且类型安全的方式解构 NHibernate 查询?是否有完全不同的方法可以实现相同的目标(考虑到无法更改数据库/服务器排序规则的相同约束)?

4

1 回答 1

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 子句。我想没有人做过,所以你要做很多实验,会有很多奇怪的查询让你大吃一惊。

于 2013-09-09T14:33:24.097 回答