如何让 NHibernate 停止使用 nvarchar(4000) 插入参数字符串?

Posted

技术标签:

【中文标题】如何让 NHibernate 停止使用 nvarchar(4000) 插入参数字符串?【英文标题】:How does one make NHibernate stop using nvarchar(4000) for insert parameter strings? 【发布时间】:2011-10-01 08:50:17 【问题描述】:

我需要优化由域实体上的保存(插入查询)生成的查询。我已经使用 Fluent NHibernate 配置了 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

如果查看 IpAddressCountryCode 的输入参数,您会注意到 NHibernate 使用的是 nvarchar(4000) .问题是nvarchar(4000) 比我需要的IpAddressCountryCode 大得多,并且由于高流量和托管要求,我需要优化数据库的内存使用。

以下是这些列的 Fluent NHibernate 自动映射覆盖:

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

这不是我看到不必要的 nvarchar(4000) 弹出的唯一地方。

我如何控制 NHibernate 使用 nvarchar(4000) 来表示字符串?

如何更改此insert 语句以使用适当大小的输入参数?

【问题讨论】:

【参考方案1】:

使用LengthType 指定为NHibernateUtil.AnsiString,而不是使用CustomSqlType。

【讨论】:

谢谢,它有效!确保您使用的是较新版本的 NHibernate 并且您使用的是最新的方言。【参考方案2】:

如果此问题强制 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");
    


【讨论】:

【参考方案3】:

好的,这就是我们要做的,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;

【讨论】:

【参考方案4】:

如果你想用 varchar 替换所有 nvarchar,这里有一个解决方法

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>())
            ...

【讨论】:

以上是关于如何让 NHibernate 停止使用 nvarchar(4000) 插入参数字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 NHibernate Validator 使用英语以外的语言?

如何让 Nhibernate 重建数据库?

Nhibernate - 如何使用 CompositeId 设计域对象和映射

如何让 NHibernate ISession 缓存主键未检索到的实体

我怎样才能让 NHibernate 只生成 SQL 而不执行它?

如何使用 nhibernate 更新主键