我有一个大型ASP.NET应用程序,它使用NHibernate(v3.2.0.4000)访问SQL Server 2005作为后端数据存储。我们有多个客户,每个客户都有自己的数据库,但所有数据库都运行在同一台服务器上。
快速提问:
如果没有在数据库服务器或各个数据库本身上设置COLLATION,或者重新编写现有的单个NHibernate查询代码,如何在NHibernate查询中添加自定义COLLATION语句,纯粹出于订购目的,在它们访问数据库之前?< /强>
问题较长:
由于拥有一些新的国际客户,我们需要支持自定义整理以便在我们的应用程序中进行分类。我们不能在数据库(或服务器)级别上执行此操作,因为我们为同一服务器上的每个客户托管一个单独的数据库(每个客户可能具有不同的排序规则要求),并且存在诸如TempDB等和多个不同的事情的复杂性同一服务器上的排序规则(请参阅here和here)。
由于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
接口,听起来很有前途,似乎对SqlString
类OnPrepareStatement
类中可以覆盖的EmptyInterceptor
方法中捕获的{{1}}进行操作,但是,我对NHibernate的这一部分完全不熟悉,并且目前没有任何帮助,正如我写的那样,NHibernate所谓的确切信息来源nhibernate.info已经失效(似乎已经持续了好几周)! / p>
有没有人必须执行这样的任务?是否有可能以干净且类型安全的方式解构NHibernate查询?是否有一种完全不同的方法可以实现相同的目标(考虑到无法改变数据库/服务器整理的相同限制)?
答案 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排序的替代品,因此通过搜索和替换,您可以替换所有出现的排序。您可以通过检查提供的条件和属性名称来安排复杂的CollationProjection,它知道何时应用排序规则。
为了实现自定义投影,阅读NHibernate源代码是获取知识的好方法,因为几乎没有文档涉及这个主题。
我不认为拦截IInterceptor.OnPrepareStatement是一个不错的选择,但如果你想继续这个方向,那么你可以尝试从原始SQL字符串的部分重建SQL字符串,嗅探ORDER BY子句处理。我想没有人做过,所以你进行了大量的实验,会有很多奇怪的问题让你感到惊讶。