我读到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_GetByName | 41092.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}'");