NHibernate – 如何解决SQL Server中的参数计数限制

我有一个网站,以简单的表格forms显示SQL Server的数据,包括filter,排序,页面导航等。我使用Fluent NHibernate作为ORM,查询代码如下所示:

public IList GetResults(UserCommand command) { var result = Session.Query(); if (command.Ids != null) result = result.WhereRestrictionOn(o => o.Id).IsIn(command.Ids); // ... other filters ... return result.Skip(command.Page * command.PageSize).Take(command.PageSize).List(); } 

问题是command.Ids (和一些其他输入参数)可能包含很多值。 如果数量超过2100,则查询执行失败,并显示以下错误

 System.Data.SqlClient.SqlException: The incoming request has too many parameters. The server supports a maximum of 2100 parameters. Reduce the number of parameters and resend the request. 

将查询拆分为具有少于2100个参数的较小块不是一个选项,因为对于多个查询,我无法知道要Skip多少条记录并显示所需页面。

还有其他减少参数数量的解决方法吗?

好吧,在看了NHibernate源代码后,我找到了一个有效的解决方案,并不是特别难看。 关键思想是在不使用参数的情况下生成SQL’IN’表达式。

首先要做的是为此表达式创建一个ICriterion类。 这应该谨慎进行,以避免可能的SQL注入。

 public class ParameterlessInExpression : AbstractCriterion { private readonly IProjection _projection; private readonly object[] _values; ///  /// Builds SQL 'IN' expression without using parameters /// NB: values must be an array of integers to avoid SQL-Injection. ///  public ParameterlessInExpression(IProjection projection, int[] values) { _projection = projection; _values = values.Select(v => (object)v).ToArray(); } public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary enabledFilters) { if (_values.Length == 0) return new SqlString("1=0"); var result = new SqlStringBuilder(); var columnNames = CriterionUtil.GetColumnNames(null, _projection, criteriaQuery, criteria, enabledFilters); for (int columnIndex = 0; columnIndex < columnNames.Length; columnIndex++) { SqlString columnName = columnNames[columnIndex]; if (columnIndex > 0) result.Add(" and "); result.Add(columnName).Add(" in (").Add(StringHelper.ToString(_values)).Add(")"); } return result.ToSqlString(); } // ... // some non-essential overrides omitted here // ... } 

接下来我们做一个很好的IQueryOver扩展

 public static IQueryOver WhereIn(this IQueryOver query, Expression> expression, int[] values) { query.UnderlyingCriteria.Add(new ParameterlessInExpression(Projections.Property(expression), values)); return query; } 

最后在查询中使用它:

 if (command.Ids != null) result = result.WhereIn(o => o.Id, command.Ids);