参数化查询的Dapper性能差

10

我正在研究将我们的EF6代码移植到Dapper以获得更好的性能,但遇到了一个奇怪的问题。单行查询在Dapper中所需的时间几乎是EF中的10倍。它看起来像这样:

using (IDbConnection conn = new SqlConnection("connection string"))
{                
      row = conn.Query<ReportView>("select * from ReportView where ID = @ID", 
                                          new {ID = id}))
                                  .FirstOrDefault();
}

这个查询针对一个包含大约80列的视图,EF版本使用完全相同的查询和模型。作为参考,以下是EF版本:

row = context.ReportViews.Where(s => s.ID == id).FirstOrDefault();

我考虑到第一次查询可能会很慢,所以在“热身”期后进行了测量。我认为重复使用EF模型可能是一个问题,因此我创建了一个简单的POCO作为模型。但这些都无效。于是我尝试了不同的方法,并决定尝试使用SQL注入连接的SQL语句。

using (IDbConnection conn = new SqlConnection("connection string"))
{                
      row = conn.Query<ReportView>(string.Format("select * from ReportView where ID = '{0}'", 
            id)).FirstOrDefault();
}

这个查询比EF查询实际上更快。

那么这里发生了什么?为什么参数化查询要慢得多?


1
分析生成的SQL语句 - stuartd
1
你如何进行基准测试? - mxmissile
@stuartd - 我该如何获取生成的 SQL? - System Down
如果您正在使用SQL Server,请使用分析器工具。我相信其他关系型数据库管理系统也有类似的工具可用。 - stuartd
@juharr - 首先,这是一个遗留应用程序,但争议的焦点不在于如何获得更好的性能,而在于为什么那个特定的Dapper查询如此缓慢? - System Down
显示剩余3条评论
2个回答

18
基于你最后的示例,看起来你的列最可能是 varchar,但当你使用参数化查询时,参数被发送为 nvarchar。由于从 nvarchar 到 varchar 可能会涉及数据丢失,SQL将表中的每个值转换为nvarchar进行比较。如你所料,转换每一行以进行比较是缓慢的,并且会防止使用索引。
为了解决这个问题,你有两个选择:
如果你的数据库根本不使用 nvarchar,则可以在应用程序启动期间简单地更改映射:
Dapper.SqlMapper.AddTypeMap(typeof(string), System.Data.DbType.AnsiString);
否则,您可以按查询更改它:
row = conn.Query<ReportView>("select * from ReportView where ID = @ID", 
                              new {ID = new DbString { Value = id, IsAnsi = true }})
                              .FirstOrDefault();

1

这与参数的数据类型有关。如果它与索引的数据类型不匹配,则将每一行强制转换为相同的类型进行比较。如果将其作为字符串处理,则SQL解析器将自动选择类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接