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

发布于 2024-11-18 21:29:15 字数 1141 浏览 3 评论 0原文

我需要优化由域实体上的保存(插入查询)生成的查询。我已经使用 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的输入参数code>,人们会注意到 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) 弹出的唯一地方。

如何控制 NHibernatenvarchar(4000) 字符串表示的使用?

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

I need to optimize a query that is being produced by a save (insert query) on a domain entity. I've configured NHibernate using Fluent NHibernate.

Here's the query generated by NHibernate during the insertion of a user's response to a poll:

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

If one looks at the input parameters for IpAddress and CountryCode, one will notice that NHibernate is using nvarchar(4000). The problem is that nvarchar(4000) is far larger than I need for either IpAddress or CountryCode and due to high traffic and hosting requirements I need to optimize the database for memory usage.

Here's the Fluent NHibernate auto-mapping overrides for those columns:

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

This isn't the only place that I see unnecessary nvarchar(4000)'s popping up.

How do I control NHibernate's usage of nvarchar(4000) for string representation?

How do I change this insert statement to use the proper sized input parameters?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

花桑 2024-11-25 21:29:15

Type 指定为 NHibernateUtil.AnsiString 并使用 Length,而不是使用 CustomSqlType。

Specify the Type as NHibernateUtil.AnsiString with a Length instead of using a CustomSqlType.

时光暖心i 2024-11-25 21:29:15

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

}

This issue can cause a huge performance problem in queries if it forces SQL Server to perform a table scan instead of using an index. We use varchar throughout our database so I created a convention to set the type globally:

/// <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");
    }

}
朕就是辣么酷 2024-11-25 21:29:15

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

Okay this is what we have to do, the SQLClientDriver ignores the length property of the SqlType. So we created a our own driverclass inheriting from SQLClientDriver and override the method GenerateCommand...Something like this:

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;
}
一江春梦 2024-11-25 21:29:15

如果您想用 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>())
            ...

Here is a work around, if you want to replace all nvarchar with 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);
    }
}

Then plug it into your config

var cfg = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)
            .Driver<Sql2008NoNVarCharDriver>())
            ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文