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

hol*_*hen 4 c# sql-server nhibernate fluent-nhibernate

我有一个网站,以简单的表格形式显示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();        
}
Run Code Online (Sandbox Code Playgroud)

问题是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.
Run Code Online (Sandbox Code Playgroud)

将查询成小块具有小于2100点的参数是不是一种选择,因为有多个查询我就没有多少记录知道该方式Skip,并Take以显示所需的页面.

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

hol*_*hen 11

好吧,在看了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
    // ...
}
Run Code Online (Sandbox Code Playgroud)

接下来我们做一个很好的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;
}
Run Code Online (Sandbox Code Playgroud)

最后在查询中使用它:

if (command.Ids != null)
    result = result.WhereIn(o => o.Id, command.Ids);
Run Code Online (Sandbox Code Playgroud)

  • 如果您的字符串值完全不可预测,那么您绝对应该找到另一种查询数据的方法,例如将查询拆分为小块,或者将值保存到临时表中,然后使用“join”。我不知道有什么安全的方法来检测随机字符串中的 SQL 注入。 (2认同)