121

Net Core下多种ORM框架特性及性能对比 - 行动派Xdpie

 6 years ago
source link: http://www.cnblogs.com/vipyoumay/p/7942621.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在.NET Framework下有许多ORM框架,最著名的无外乎是Entity Framework,它拥有悠久的历史以及便捷的语法,在占有率上一路领先。但随着Dapper的出现,它的地位受到了威胁,本文对比了在.NET Core下 两种框架的表现以及与原生的ADO.NET 的对比。

1.1. Dapper是什么

Dapper是.NET的简单对象映射器,在速度方面拥有微型ORM之王的称号,几乎和使用原始ADO.NET数据读取器一样快。 ORM是一个对象关系映射器,它负责数据库和编程语言之间的映射。Dapper是通过对IDbConnection接口的扩展来操作数据库的。

1.2. 为什么选择Dapper

1.2.1. Dapper和Entity Framework Core的比较

Dapper近来越来越受到开发者们的青睐,得益于它的小巧轻便

名称 包大小 语法支持
Dapper 165kb Sql
Dapper Contrib 50kb 方法
EntityFramework Core 1M+ Sql和Linq

从语法的支持来看,EntityFrameworkCore更具优势,Linq编写时能获取开发工具更友好的提示,相比直接写sql字符串更早的知道哪里出现错误。

这里笔者使用了两台计算机(都为台式机),一台为Mysql数据库(Mysql版本:5.7.20)服务器,另一台为程序服务器。两台服务器的配置如下:

服务器 操作系统 内存 处理器 内核
MySql数据库 windows 10 64bit 16G i7 8核
程序服务器 windows 10 64bit 8G i3 4核

以下是对5002条数据的查询测试,测试时分两种情况,第一种是在预热的情况下查询,第二种是在不预热的情况下执行查询;查询时用三种操作数据库的方式,第一个是用原生的ADO.Net、第二个是微软提供的Entity Framework、第三个是Dapper其中又使用Dapper使用Sql和方法的方式进行查询数据库;分别对四种不种的情况进行查询:

  1. 通过500次循环每次获取一条数据,并且将数据映射成POCO所花费的总时间
  2. 通过500次循环每次获取一条数据,并且将数据映射为动态对象所花费的总时间
  3. 一次性取出5000条(实际为5002)数据,并且将数据映射成POCO所花费的总时间
  4. 一次性取出5000条(实际为5002)数据,并且将数据映射成动态对象所花费的总时间

为了让数据更真实一点,预热和不预热的情况分别执行了多次(每种情况超过10次),以下为笔者分别取其中两次,其它情况基本相似

预热情况:

从上面测试结果可以得出(速度由快到慢)

1. 循环获取单条数据

ADO≈Dapper Query(Buffered)≈Dapper Query(First Or Default)≈Dapper Contrib>Entity Framework>Dapper Query(Non Buffered)

2. 一次性获取5000多条数据

一次性获取整张表的数据性能比较接近

下面是用WireShark分别对几种情况连接数据时抓取数据:

ADO.Net

Entity Framework:

Dapper (Non Buffered)

Dapper (First Or Default)

Dapper(Buffered):

从以上面图中可以得出以下结论:

  1. ADO.Net、Dapper(Buffered)、Dapper(First Or Default)、Dapper(Non Buffered)、Dapper Contrib在建立连接到第一次取数据的时候是一样的,所以效率上它们之间差不多

  2. Entity Framework在第一次获取数据慢是因为作了一次重连

  3. Dapper (Non Buffered)在500次循环获取数据时速度最慢,是因为每次请求完成以后它都会释放连接,下次请求时再建立连接

生成Sql语句

Dapper

object param = new { Id = Id };
            return conn.Query<Post>("select * from Posts where Id=@Id", param, buffered: true).First();

生成的Sql


select * from Posts where Id = 501

Entity Framework


ctx.Posts.FirstOrDefault(p => p.Id == Id) as Post;

生成的Sql语句


SELECT `p`.`Id`, `p`.`Counter1`, `p`.`Counter2`, `p`.`Counter3`, `p`.`Counter4`, `p`.`Counter5`, `p`.`Counter6`, `p`.`Counter7`, `p`.`Counter8`, `p`.`Counter9`, `p`.`CreationDate`, `p`.`LastChangeDate`, `p`.`Text`
FROM `Posts` AS `p`
WHERE `p`.`Id` = 501
LIMIT 1

|框架|语法|操作|性能|数据支持|迁移成本|
|----|:----😐:----😐:----😐:----😐:----😐:----😐
|ADO.Net|Sql|复杂|好|不限|高|
|Dapper|Sql和方法|一般|好|主流关系数据库(可扩展支持NoSql)|一般|
|Entity Framework|Sql和Linq|简单|一般|主流关系数据库|低|

笔者这里使用的是一个开源测试代码源码下载,原作者使用的是.Net Framework进行,要将其修改为.Net Core。但是几乎不需要修改什么代码;只需要引用.Net Core需要的包就可以了。但是由于笔者这里用的数据库是Mysql,所以需要修改ADO.Net连接地方,这也是ADO操作数据不好的地方换一个数据库就要改一次代码,具体修改如下:

Sql Server


var idParam = cmd.Parameters.Add("@Id", System.Data.SqlDbType.Int);

MySql


 var idParam = cmd.Parameters.Add("@Id", System.Data.DbType.Int32);

Sql Server


using (var reader = cmd.ExecuteReader())
{
    reader.Read();
    obj = new Post {
        Id = reader.GetInt32(0),
        Text = reader.GetNullableString(1),
        CreationDate = reader.GetDateTime(2),
        LastChangeDate = reader.GetDateTime(3),
        Counter1 = reader.GetNullableValue<int>(4),
        Counter2 = reader.GetNullableValue<int>(5),
        Counter3 = reader.GetNullableValue<int>(6),
        Counter4 = reader.GetNullableValue<int>(7),
        Counter5 = reader.GetNullableValue<int>(8),
        Counter6 = reader.GetNullableValue<int>(9),
        Counter7 = reader.GetNullableValue<int>(10),
        Counter8 = reader.GetNullableValue<int>(11),
        Counter9 = reader.GetNullableValue<int>(12),
    };
}

MySql


using (var reader = cmd.ExecuteReader())
{
    reader.Read();
    obj = new Post {
        Id = reader.GetInt32(0),
        Text = reader.GetString(1),
        CreationDate = reader.GetDateTime(2),
        LastChangeDate = reader.GetDateTime(3),
        Counter1 = reader.GetValue(4) as int?,
        Counter2 = reader.GetValue(5) as int?,
        Counter3 = reader.GetValue(6) as int?,
        Counter4 = reader.GetValue(7) as int?,
        Counter5 = reader.GetValue(8) as int?,
        Counter6 = reader.GetValue(9) as int?,
        Counter7 = reader.GetValue(10) as int?,
        Counter8 = reader.GetValue(11) as int?,
        Counter9 = reader.GetValue(12) as int?,
    };
}

修改EntityFrameworkExecuter


public Post GetItemAsObject(int Id)
{
    return ctx.Posts.Where(p => p.Id == Id) as Post;
}


public Post GetItemAsObject(int Id)
{
    return ctx.Posts.FirstOrDefault(p => p.Id == Id);
}


public Post GetItemAsObject(int Id)
{
    return ctx.Posts.Where(p => p.Id == Id).FirstOrDefault();
}

因为Posts.Where(p => p.Id == Id)想让它生成的结果是:


SELECT `p`.`Id`, `p`.`Counter1`, `p`.`Counter2`, `p`.`Counter3`, `p`.`Counter4`, `p`.`Counter5`, `p`.`Counter6`, `p`.`Counter7`, `p`.`Counter8`, `p`.`Counter9`, `p`.`CreationDate`, `p`.`LastChangeDate`, `p`.`Text`
FROM `Posts` AS `p`
WHERE `p`.`Id` = 501

但是结果却是:


SELECT `p`.`Id`, `p`.`Counter1`, `p`.`Counter2`, `p`.`Counter3`, `p`.`Counter4`, `p`.`Counter5`, `p`.`Counter6`, `p`.`Counter7`, `p`.`Counter8`, `p`.`Counter9`, `p`.`CreationDate`, `p`.`LastChangeDate`, `p`.`Text`
FROM `Posts` AS `p`

由于官方Mysql的Entity Framework存在问题,这里使用了第三方的Pomelo.EntityFrameworkCore.MySql

1.3. 总结

本文中使用原生ADO.Net和轻量级ORM框架Dapper和官方提供的ORM框架Entity Framework Core同时操作Mysql数据时循环500次查询一条数据时所耗时间和一次性取5000条数据所消耗时间比较;如果你喜欢Sql或你的数据库不复杂,那么Dapper是你的不二之选,它性能接近原生ADO.Net有些地方还要更优于,操作也比较方便。

https://github.com/StackExchange/Dapper

http://dapper-tutorial.net/dapper

作者:xdpie 出处: http://www.cnblogs.com/vipyoumay/p/7942621.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK