当我遇到一个奇怪的问题时,我正在研究将一些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更快。
那么这里发生了什么?为什么参数化查询这么慢?
根据您的最后一个示例,您的列似乎很可能是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();