Как заставить NHibernate прекратить использовать nvarchar (4000) для вставки строк параметров? - PullRequest
10 голосов
/ 08 июля 2011

Мне нужно оптимизировать запрос, который создается при сохранении (вставка запроса) в объекте домена. Я настроил NHibernate, используя Fluent NHibernate.

Вот запрос, сгенерированный NHibernate во время вставки ответа пользователя на опрос:

exec sp_executesql N'INSERT INTO dbo.Response (ModifiedDate, IpAddress, CountryCode, 
IsRemoteAddr, PollId) VALUES (@p0, @p1, @p2, @p3, @p4); select SCOPE_IDENTITY()',N'@p0
datetime,@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 bit,@p4 int',
@p0='2001-07-08 03:59:05',@p1=N'127.0.0.1',@p2=N'US',@p3=1,@p4=2

Если посмотреть на входные параметры для IpAddress и CountryCode, можно заметить, что NHibernate использует nvarchar(4000). Проблема в том, что nvarchar(4000) намного больше, чем мне нужно для IpAddress или CountryCode, и из-за высоких требований к трафику и хостингу мне нужно оптимизировать базу данных для использования памяти.

Вот переопределения автоматического сопоставления Fluent NHibernate для этих столбцов:

    mapping.Map(x => x.IpAddress).CustomSqlType("varchar(15)");
    mapping.Map(x => x.CountryCode).CustomSqlType("varchar(6)");

Это не единственное место, где я вижу всплывающие ненужные nvarchar (4000).

Как мне контролировать NHibernate использование nvarchar(4000) для представления строки?

Как мне изменить этот оператор insert, чтобы использовать входные параметры правильного размера?

Ответы [ 4 ]

9 голосов
/ 08 июля 2011

Укажите Type как NHibernateUtil.AnsiString с Length вместо использования CustomSqlType.

4 голосов
/ 09 июля 2011

Эта проблема может вызвать огромную проблему производительности в запросах, если она заставляет SQL Server выполнять сканирование таблицы вместо использования индекса.Мы используем varchar в нашей базе данных, поэтому я создал соглашение для глобальной установки типа:

/// <summary>
/// Convert all string properties to AnsiString (varchar). This does not work with SQL CE.
/// </summary>
public class AnsiStringConvention : IPropertyConventionAcceptance, IPropertyConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.Equals(typeof(string)));
    }

    public void Apply(IPropertyInstance instance)
    {
        instance.CustomType("AnsiString");
    }

}
2 голосов
/ 11 апреля 2013

Хорошо, это то, что мы должны сделать, SQLClientDriver игнорирует свойство длины SqlType.Поэтому мы создали наш собственный класс драйвера, унаследованный от SQLClientDriver, и переопределили метод GenerateCommand ... Примерно так:

public override IDbCommand GenerateCommand(CommandType type, NHibernate.SqlCommand.SqlString sqlString, SqlType[] parameterTypes)
{
    var dbCommand = base.GenerateCommand(type, sqlString, parameterTypes);
    SetParameterSizes(dbCommand.Parameters, parameterTypes);
    return dbCommand;
}

private static void SetParameterSizes(IDataParameterCollection parameters, SqlType[] parameterTypes)
{
    for (int index = 0; index < parameters.Count; ++index)
        SetVariableLengthParameterSize((IDbDataParameter)parameters[index], parameterTypes[index]);
}

private static void SetVariableLengthParameterSize(IDbDataParameter dbParam, SqlType sqlType)
{
    SetDefaultParameterSize(dbParam, sqlType);
    if (sqlType.LengthDefined && !IsText(dbParam, sqlType) && !IsBlob(dbParam, sqlType))
        dbParam.Size = sqlType.Length;
    if (!sqlType.PrecisionDefined)
        return;
    dbParam.Precision = sqlType.Precision;
    dbParam.Scale = sqlType.Scale;
}
1 голос
/ 27 ноября 2017

Вот обходной путь, если вы хотите заменить все nvarchar на varchar

public class Sql2008NoNVarCharDriver : Sql2008ClientDriver
{
    public override void AdjustCommand(IDbCommand command)
    {
        foreach (System.Data.SqlClient.SqlParameter x in command.Parameters)
        {
            if (x.SqlDbType == SqlDbType.NVarChar)
            {
                x.SqlDbType = SqlDbType.VarChar;
            }
        }
        base.AdjustCommand(command);
    }
}

Затем подключите его к вашей конфигурации

var cfg = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)
            .Driver<Sql2008NoNVarCharDriver>())
            ...
...