我有一个网站,以简单的表格的形式显示SQL Server的数据,包括过滤器,排序,页面导航等。 我使用Fluent NHibernate作为ORM,查询代码如下所示:
public IList<Operation> GetResults(UserCommand command)
{
var result = Session.Query<Operation>();
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
和Take
以显示所需的页面
还有其他减少参数数量的解决方法吗?
答案 0 :(得分:8)
好吧,在看了NHibernate源代码后,我找到了一个有效的解决方案,并不是特别难看。关键思想是在不使用参数的情况下生成SQL'IN'表达式。
要做的第一件事是为此表达式创建一个ICriterion类。这应该谨慎进行,以避免可能的SQL注入。
public class ParameterlessInExpression : AbstractCriterion
{
private readonly IProjection _projection;
private readonly object[] _values;
/// <summary>
/// Builds SQL 'IN' expression without using parameters
/// NB: values must be an array of integers to avoid SQL-Injection.
/// </summary>
public ParameterlessInExpression(IProjection projection, int[] values)
{
_projection = projection;
_values = values.Select(v => (object)v).ToArray();
}
public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary<string, IFilter> 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<TRoot, TSubType> WhereIn<TRoot, TSubType>(this IQueryOver<TRoot, TSubType> query, Expression<Func<TSubType, object>> expression, int[] values)
{
query.UnderlyingCriteria.Add(new ParameterlessInExpression(Projections.Property<TSubType>(expression), values));
return query;
}
最后在查询中使用它:
if (command.Ids != null)
result = result.WhereIn(o => o.Id, command.Ids);