3

我有 NH 和 FNH 的主干版本。当我尝试添加二级缓存时,NHibernate 的某些部分忘记了选择的 sqldialect。


初始配置:

var cfg = Fluently.Configure()
  .Database(MsSqlConfiguration.MsSql2008
    .ConnectionString(connectionString)
    .DefaultSchema("dbo")
    .UseReflectionOptimizer()    
  .Mappings(m => ................);

有罪自定义查询:

var sql = @"with Foo(col1,col2,col3)
              as (select bla bla bla...)
            Select bla bla bla from Foo";

list = Session.CreateSQLQuery(sql)
  .AddEntity("fizz", typeof(Fizz))
  .SomethingUnimportant();

当我将配置更改为:

var cfg = Fluently.Configure()
  .Database(MsSqlConfiguration.MsSql2008
    .ConnectionString(connectionString)
    .DefaultSchema("dbo")
     .UseReflectionOptimizer()
     .Cache(c=>c
       .UseQueryCache()
         .ProviderClass<HashtableCacheProvider>())
       .ShowSql())
     .Mappings(m => ................);

查询抛出错误(WITH在mssql2008中添加了子句):

查询应以“SELECT”或“SELECT DISTINCT”开头

[NotSupportedException: 查询应该以 'SELECT' 或 'SELECT DISTINCT' 开头] NHibernate.Dialect.MsSql2000Dialect.GetAfterSelectInsertPoint(SqlString sql) +179 NHibernate.Dialect.MsSql2000Dialect.GetLimitString(SqlString querySqlString, Int32 offset, Int32 limit) +119 NHibernate .Dialect.MsSql2005Dialect.GetLimitString(SqlString querySqlString, Int32 offset, Int32 last) +127 NHibernate.Loader.Loader.PrepareQueryCommand(QueryParameters queryParameters, Boolean scroll, ISessionImplementor session) +725 NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters , Boolean returnProxies) +352 NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +114 NHibernate.Loader。Loader.DoList(ISessionImplementor session, QueryParameters queryParameters) +205


任何想法究竟是什么混淆了 nhibernate 以及如何解决它?


有罪的 NHibernate 代码(在 NHibernate/Dialect/MsSql200Dialect.cs 中):

private static int GetAfterSelectInsertPoint(SqlString sql)
{
  if (sql.StartsWithCaseInsensitive("select distinct"))
  {
    return 15;
  }
  else if (sql.StartsWithCaseInsensitive("select"))
  {
    return 6;
  }
  throw new NotSupportedException
    ("The query should start with 'SELECT' or 'SELECT DISTINCT'");
  }
}

看起来是.SetMaxResults(123)导致这个的。幸运的是,我可以取消绑定该查询。

希望这能解决这个问题。

4

7 回答 7

4

我使用 Alkampfer 的解决方案修复了该错误,但我创建了自己的 SQL 方言,而不是直接修补 NHibernate 源:

public class Sql2008DialectWithBugFixes : MsSql2008Dialect
{
    public override SqlString GetLimitString(SqlString querySqlString, int offset, int last)
    {
        if (offset == 0)
        {
            return querySqlString.Insert(GetAfterSelectInsertPoint(querySqlString), " top " + last);
        }

        return base.GetLimitString(querySqlString, offset, last);
    }

    private static int GetAfterSelectInsertPoint(SqlString sql)
    {
        Int32 selectPosition;

        if ((selectPosition = sql.IndexOfCaseInsensitive("select distinct")) >= 0)
        {
            return selectPosition + 15; // "select distinct".Length;

        }
        if ((selectPosition = sql.IndexOfCaseInsensitive("select")) >= 0)
        {
            return selectPosition + 6; // "select".Length;
        }

        throw new NotSupportedException("The query should start with 'SELECT' or 'SELECT DISTINCT'");
    }
}
于 2011-12-12T04:53:18.307 回答
3

我有一个类似的问题(删除SetMaxResults也有帮助,但我需要分页)并发现以下 NHibernate 配置属性导致了这个错误:

<property name="use_sql_comments">true</property>

这肯定是一个错误,因为该GetAfterSelectInsertPoint方法没有考虑到 SQL 注释可能会附加到 SQL 查询中。

只需将use_sql_comments属性设置为false,问题就消失了。

于 2011-01-21T15:05:50.743 回答
2

使用具有 WITH 子句的类似查询时遇到了同样的问题。

不幸的是,我的查询填充了一个带有分页的网格,所以我必须保留 SetMaxResults。

我的解决方案是使用派生表重写:

var sql = @"with Foo(col1,col2,col3)
              as (select x1, x2, x3 from x join y blabla)
            Select col1, col2, col3 from Foo
            join B on B.col1 = Foo.col1";

变成

var sql = @"Select col1, col2, col3 from 
           (select x1 as col1, x2 as col2, x3 as col3 
            from x join y blabla) as Foo
           join B on B.col1 = Foo.col1";

只是为了让 NHibernate 在“select”字符串之后插入“TOP x”字符串(从开头开始的 6 个字符)......没有评论:(

于 2010-10-15T14:41:20.320 回答
2

Sandor 告诉我们,用于在查询中查找插入 TOP 子句 (GetAfterSelectInsertPoint) 的位置的例程中似乎存在一些奇怪的错误。您可以直接在 nh 源中修复它(我实际上修补了我在项目中使用的 2.1 版本,您可以在此处找到详细信息)。因此,如果您绝对需要使用 use_sql_comments 启用评论,您可以:)

于 2011-07-28T12:30:04.643 回答
2

我从 1.2 升级到 3.2 时遇到了这个问题(我知道,BIG jump eh?)。

我的问题是在 hql 中的 select 语句前面有一个前导空格,例如 String hql = " select "...

使用 SQL2005 方言,这会崩溃并显示“System.NotSupportedException:查询应以 'SELECT'... 开始”消息。

解决方案是

  1. 创建一个失败的单元测试,一个好的测试驱动开发人员应该:)
  2. 从“select...”语句中删除前导空格
  3. 构建并运行单元测试
于 2012-05-24T20:40:14.653 回答
0

正如我预测的那样 - 无界选择是可接受的解决方法。

已删除SetMaxResults并且有效。

于 2010-03-17T11:42:08.167 回答
0

我们在升级到 NHibernate 3.3 版时遇到了这个问题,但出于不同的原因......空白。我们有很多看起来像这样的 sql 字符串:

var sql = @"
select col1 from MyTable";

或者:

var sql = @" select col1 from My Table";

这些导致“查询应该以'SELECT'或'SELECT DISTINCT'开始”错误,因为NHibernate在验证之前不会修剪字符串。

我们创建了一个新的方言,首先修剪字符串来解决这个问题:

public class Sql2008DialectCustom : MsSql2008Dialect
{
  public override SqlString GetLimitString(SqlString queryString, SqlString offset, SqlString limit)
  {
    var trimmedQueryString = queryString.Trim();
    return base.GetLimitString(trimmedQueryString, offset, limit);
  }
}
于 2014-07-23T19:01:12.943 回答