您的位置:首页 > 移动开发

Entity Framwork Vs ADO .Net Vs Dapper .Net 效率比较

2017-12-11 09:27 435 查看
由我另一个博客拷贝

近年来一直使用EF+.Net MVC来编写系统,由于最近换了一个公司,碰到一个印度同事,他告诉我用Dapper比用EF高效很多,基本可以接近ADO .Net 的效率,因为习惯的问题一直没有对ORM做深入的了解的解析,所以借此机会对此三种方法进行效率测试,以便今后做大型数据处理时可以有更多的选择性。测试用例借鉴了 here

比较版本

EF 6.2.0, Dapper 1.50.4

数据表结构



public class Sport
{
public Sport()
{
Teams = new HashSet<Team>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Team> Teams { get; set; }
}


public class Team
{
public Team()
{
Players = new HashSet<Player>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }

public DateTime FoundingDate { get; set; }
[ForeignKey("Sport")]
public int SportId { get; set; }
public Sport Sport { get; set; }
public ICollection<Player> Players { get; set; }
}


public class Player
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string  FirstName { get; set; }
public string  LastName { get; set; }
public DateTime DateOfBirth { get; set; }
[ForeignKey("Team")]
public int TeamId { get; set; }
public Team Team { get; set; }
}


数据

Sports表加入47个不同的运动项目,大家可以网上自行搜索后加入,

Teams随即生产1000个Team,并随机分配到Sport

Players随机生成100万个Player,并随机分配到Team

数据库由EF Code First生成,并且使用比较熟悉的EF加入数据

查询Interface.

public interface ITestORM
{
long GetPlayerByID(int id);
long GetPlayersForTeam(int teamId);
long GetPlayersForSport(int sportId);
}


GetPlayerByID: 根据运动员编号(主键)查找运动员

GetPlayersForTeam: 查找给定Team编号的所有运动员

GetPlayersForSport: 查找给定Sport编号的所有运动员

所以方法都不返回查找的结果,只是返回一个tick值(一千万分之一秒)

每个方法运行10次,并且取得平均值和搜寻的总时长进行比较

Entity Framework 实现代码

public class EFORM : ITestORM
{
private bool _tracking = true;
public EFORM()
{

}
public EFORM(bool tracking)
{
_tracking = tracking;
}
public long GetPlayerByID(int id)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (TestORMContext context = new TestORMContext())
{
if (!_tracking)
{
var player = context.Players.AsNoTracking().Where(x => x.Id == id).First();
}
else
{
var player = context.Players.Where(x => x.Id == id).First();
}

}
watch.Stop();
return watch.ElapsedMilliseconds;
}

public long GetPlayersForTeam(int teamId)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (TestORMContext context = new TestORMContext())
{
if (!_tracking)
{
var players = context.Players.AsNoTracking().Where(x => x.TeamId == teamId).ToList();
}
else
{
var players = context.Players.Where(x => x.TeamId == teamId).ToList();
}
}
watch.Stop();
return watch.ElapsedMilliseconds;
}

public long GetPlayersForSport(int sportId)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (TestORMContext context = new TestORMContext())
{
if (!_tracking)
{
var players = context.Players.AsNoTracking().Where(player => player.Team.Sport.Id == sportId).ToList();
}
else
{
var players = context.Players.Where(player => player.Team.Sport.Id == sportId).ToList();
}
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
}


创建EF查询对象时可以用
new EFROM()
–可tracking和
new EFROM(false)
–不可tracking进行查询,以便区分打开和关闭tracking时所需的查询时间,因为在上述网站上有网友提出不加tracking时,EF效率也很高效

ADO .Net 实现代码

public class ADO:ITestORM
{
public long GetPlayerByID(int id)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TestORM"].ToString()))            {
conn.Open();
using (SqlDataAdapter adapter = new SqlDataAdapter("SELECT Id, FirstName, LastName, DateOfBirth, TeamId FROM Players WHERE Id = @ID", conn))
{
adapter.SelectCommand.Parameters.Add(new SqlParameter("@ID", id));
DataTable table = new DataTable();
adapter.Fill(table);
}
}
watch.Stop();
return watch.ElapsedMilliseconds;
}

public long GetPlayersForTeam(int teamId)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TestORM"].ToString()))
{
conn.Open();
using (SqlDataAdapter adapter = new SqlDataAdapter("SELECT Id, FirstName, LastName, DateOfBirth, TeamId FROM Players WHERE TeamId = @ID", conn))
{
adapter.SelectCommand.Parameters.Add(new SqlParameter("@ID", teamId));
DataTable table = new DataTable();
adapter.Fill(table);
}
}
watch.Stop();
return watch.ElapsedMilliseconds;
}

public long GetPlayersForSport(int sportId)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TestORM"].ToString()))
{
conn.Open();
using (SqlDataAdapter adapter = new SqlDataAdapter("SELECT p.Id, p.FirstName, p.LastName, p.DateOfBirth, p.TeamId, t.Id as TeamId, t.Name, t.SportId FROM Players p INNER JOIN Teams t ON p.TeamId = t.Id WHERE t.SportId = @ID", conn))
{
adapter.SelectCommand.Parameters.Add(new SqlParameter("@ID", sportId));
DataTable table = new DataTable();
adapter.Fill(table);
}
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
}


Dapper .Net 实现代码

public class DapperORM:ITestORM
{
public long GetPlayerByID(int id)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TestORM"].ToString()))
{
conn.Open();
var player = conn.Query<Player>("SELECT Id, FirstName, LastName, DateOfBirth, TeamId FROM Players WHERE Id = @ID", new { ID = id });
}
watch.Stop();
return watch.ElapsedMilliseconds;
}

public long GetPlayersForTeam(int teamId)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TestORM"].ToString()))
{
conn.Open();
var players = conn.Query<List<Player>>("SELECT Id, FirstName, LastName, DateOfBirth, TeamId FROM Players WHERE TeamId = @ID", new { ID = teamId });
}
watch.Stop();
return watch.ElapsedMilliseconds;
}

public long GetPlayersForSport(int sportId)
{
Stopwatch watch = new Stopwatch();
watch.Start();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["TestORM"].ToString()))
{
conn.Open();
var players = conn.Query<Player, Team, Player>("SELECT p.Id, p.FirstName, p.LastName, p.DateOfBirth, p.TeamId, t.Id as TeamId, t.Name, t.SportId FROM Teams t "
+ "INNER JOIN Players p ON t.Id = p.TeamId WHERE t.SportId = @ID", (player, team) => { return player; }, splitOn: "TeamId", param: new { ID = sportId });
}
watch.Stop();
return watch.ElapsedMilliseconds;
}
}


代码说明

上述代码基本都借鉴于 此处,不是完全拷贝,两者有部分地方有区别

测试说明

每种方法中的每个查询函数分别运行10次,每次查询的id为总记录的1/10,分别记录每个查询的时长,EF查询两次,一次tracking,一次no tracking

测试结果

说明: 运动员的编号没有从1开始,所以有下面奇怪的运动员编号 :P

EF 有 Tracking

查询 1Tick查询 2Tick查询 3Tick
GetPlayerByID(1006820)1801GetPlayersForTeam(1)13GetPlayersForSport(1)501
GetPlayerByID(1106820)1GetPlayersForTeam(101)11GetPlayersForSport(5)312
GetPlayerByID(1206820)1GetPlayersForTeam(201)11GetPlayersForSport(9)284
GetPlayerByID(1306820)1GetPlayersForTeam(301)20GetPlayersForSport(13)363
GetPlayerByID(1406820)1GetPlayersForTeam(401)24GetPlayersForSport(17)457
GetPlayerByID(1506820)1GetPlayersForTeam(501)11GetPlayersForSport(21)281
GetPlayerByID(1606820)0GetPlayersForTeam(601)14GetPlayersForSport(25)417
GetPlayerByID(1706820)1GetPlayersForTeam(701)17GetPlayersForSport(29)344
GetPlayerByID(1806820)1GetPlayersForTeam(801)38GetPlayersForSport(33)266
GetPlayerByID(1906820)1GetPlayersForTeam(901)11GetPlayersForSport(37)327
平均180.917355.2
总时长5531

EF 无 Tracking

查询 1Tick查询 2Tick查询 3Tick
GetPlayerByID(1006820)1760GetPlayersForTeam(1)4GetTeamsForSport(1)247
GetPlayerByID(1106820)1GetPlayersForTeam(101)3GetTeamsForSport(5)180
GetPlayerByID(1206820)1GetPlayersForTeam(201)3GetTeamsForSport(9)171
GetPlayerByID(1306820)1GetPlayersForTeam(301)3GetTeamsForSport(13)172
GetPlayerByID(1406820)1GetPlayersForTeam(401)4GetTeamsForSport(17)197
GetPlayerByID(1506820)1GetPlayersForTeam(501)3GetTeamsForSport(21)171
GetPlayerByID(1606820)1GetPlayersForTeam(601)3GetTeamsForSport(25)179
GetPlayerByID(1706820)1GetPlayersForTeam(701)3GetTeamsForSport(29)181
GetPlayerByID(1806820)1GetPlayersForTeam(801)3GetTeamsForSport(33)172
GetPlayerByID(1906820)1GetPlayersForTeam(901)3GetTeamsForSport(37)175
平均1773185
总时长3646

ADO .Net

查询 1Tick查询 2Tick查询 3Tick
GetPlayerByID(1006820)79GetPlayersForTeam(1)4GetPlayersForSport(1)1834
GetPlayerByID(1106820)0GetPlayersForTeam(101)3GetPlayersForSport(5)184
GetPlayerByID(1206820)0GetPlayersForTeam(201)3GetPlayersForSport(9)180
GetPlayerByID(1306820)0GetPlayersForTeam(301)3GetPlayersForSport(13)184
GetPlayerByID(1406820)0GetPlayersForTeam(401)3GetPlayersForSport(17)207
GetPlayerByID(1506820)0GetPlayersForTeam(501)3GetPlayersForSport(21)192
GetPlayerByID(1606820)0GetPlayersForTeam(601)3GetPlayersForSport(25)194
GetPlayerByID(1706820)0GetPlayersForTeam(701)3GetPlayersForSport(29)182
GetPlayerByID(1806820)0GetPlayersForTeam(801)3GetPlayersForSport(33)191
GetPlayerByID(1906820)0GetPlayersForTeam(901)3GetPlayersForSport(37)184
平均7.93.1353.2
总时长3642

Dapper .Net

查询 1Tick查询 2Tick查询 3Tick
GetPlayerByID(1006820)129GetPlayersForTeam(1)4GetPlayersForSport(1)214
GetPlayerByID(1106820)0GetPlayersForTeam(101)2GetPlayersForSport(5)179
GetPlayerByID(1206820)0GetPlayersForTeam(201)2GetPlayersForSport(9)176
GetPlayerByID(1306820)0GetPlayersForTeam(301)2GetPlayersForSport(13)185
GetPlayerByID(1406820)0GetPlayersForTeam(401)2GetPlayersForSport(17)194
GetPlayerByID(1506820)0GetPlayersForTeam(501)2GetPlayersForSport(21)182
GetPlayerByID(1606820)0GetPlayersForTeam(601)2GetPlayersForSport(25)189
GetPlayerByID(1706820)0GetPlayersForTeam(701)9GetPlayersForSport(29)189
GetPlayerByID(1806820)0GetPlayersForTeam(801)2GetPlayersForSport(33)176
GetPlayerByID(1906820)0GetPlayersForTeam(901)2GetPlayersForSport(37)181
平均12.92.9186.5
总时长2023

比较结果

*EF W TrackingEF WO TrackingADO.NetDapper.Net
GetPlayerByID180.9176.97.912.9
GetPlayersForTeam173.23.12.9
GetPlayersForSport355.2184.5353.2186.5
Total Time5531364636422023
感觉Dapper真的非常高效,No Tracking的EF效率也不低和ADO .Net不相上下,不过对于在系统中嵌套SQL语言,个人比较不好接受,而且不优雅,对今后维护工作也有一定麻烦,因此今后采用Dapper机会应该不多,除非一些不需要后期维护而且数据量庞大的系统或许会采用。不过仁者见仁,智者见智,当前Dapper好像非常火,但是不管用哪种方法,达到目的,而且效率不要过份低下都不失为一个好的方法。

Richard 于 2017圣诞前
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息