Dapper 的 QueryFirstOrDefault 方法真的很慢吗?



我读到Dapper比EF更快(至少在检索数据方面(,我想确认这一点,所以我正在BenchmarkDotNet的帮助下比较Dapper和EntityFramework。

所以我尝试了这个。。。

[Benchmark]
public Player EntityFramework_GetByName()
{
using (ApplicationDbContext context = new())
{
return context.Players.FirstOrDefault(x => x.FirstName == _name);
}
}
[Benchmark]
public Player Dapper_GetByName()
{
using (SqlConnection conn = new(Database.ConnectionString))
{
return conn.QueryFirstOrDefault<Player>($"SELECT * FROM Players WHERE FirstName = '{_name}'");
}
}

但结果并不是我所期望的。。。

然后我在这里读到关于列类型";"问题";以及这会如何影响性能,所以我将列的类型更改为最大长度为100的NVarchar,并将Dapper的代码更改为这个

[Benchmark]
public Player Dapper_GetByName()
{
using (SqlConnection conn = new(Database.ConnectionString))
{
return conn.QueryFirstOrDefault<Player>($"SELECT * FROM Players WHERE FirstName = @name", new 
{ @name = new DbString { Value = _name, IsAnsi = false } });
}
}

基准测试的结果如下。。

方法平均值StdDev
Dapper_GetByName41092.8 us

我认为这个例子非常清楚地表明了直接使用Dapper、CA.Blocks.DataAccess或ADO.NET时SQL查询生成的责任。当使用这些包访问数据库时,开发人员完全负责SQL查询、它的投影和执行。当使用EF时,生成查询的责任从开发人员身上删除,并委托给EF。这是一把双刃剑,既可能导致好的查询,也可能导致非常糟糕的查询。Dapper的大部分性能提升来自于对SQL的完全控制和消除错误的SQL生成。反之亦然,与EF相比,Dapper的大多数性能问题都是由于EF创建了更好的查询。这里发生了什么。简单地说,EF已经查看了请求,并知道您只记录了第一条FirstOrDefault,因此其查询生成已导致

SELECT TOP 1 * FROM … WHERE…

您正在进行比较的Dapper查询是

SELECT * FROM … WHERE …

因此,我怀疑区别纯粹在于SQL。所使用的测试数据库可能在Person表中有许多记录。考虑到这些数字,很可能没有对名称进行索引,从而导致表扫描来查找匹配的数据。

在EF生成的查询中,数据库可以在找到第一条记录后立即停止执行,在Dapper示例中,数据库根据名称组装具有所有匹配项的完整记录集,然后发送该行集。Dapper只是读取第一行并关闭连接。

为了进行公平的比较,您需要将查询更改为top 1。像

[Benchmark]
public Player Dapper_GetByName()
{
using (SqlConnection conn = new(Database.ConnectionString))
{
return conn.QueryFirstOrDefault<Player>($"SELECT Top 1 * FROM Players WHERE FirstName = @name", new 
{ @name = new DbString { Value = _name, IsAnsi = false } });
}
}

此外,决定使用Dapper来提高性能意味着您需要了解和喜爱SQL。

嗯,也许你不应该比较

// Open and Close a completely new database connection
using (SqlConnection conn = new(Database.ConnectionString))

// Create a new Unit of Work / Transaction
using (ApplicationDbContext context = new())

仅对内部部分进行基准测试:

return conn.QueryFirstOrDefault<Player>($"SELECT * FROM Players WHERE FirstName = '{_name}'");

相关内容

  • 没有找到相关文章

最新更新