我們都知道,ORM全稱是,Object Relationship Mapper,即,對象關系映射。也就是可以用object來map我們的db,而且市面上的orm框架有很多,其中有一個框架叫做dapper,而且被稱為the king of ORM。
市場上,也有一些其他的ORM,比如EF Core,NHibernate 、FreeSQL等等,來處理大數據訪問及關系映射。既然官方推出了EF Core,說明其對框架的支持會很友好,為什么又會有那么多的ORM框架供我們使用呢?其實,每一個框架都有其適用的場景。如果你在小的項目中,使用Entity Framework、Entity Framework Core、NHibernate 來處理大數據訪問及關系映射,未免有點殺雞用牛刀。你又覺得ORM省時省力,這時Dapper 將是你不二的選擇。其實,Entity Framework Core的性能並不是很高,當對性能有要求的時候,一般公司都會自己封裝一套ORM。
為什么選擇Dapper?
- 輕量。只有一個文件(SqlMapper.cs),編譯完成之后只有120k(好象是變胖了)
- 速度快。Dapper的速度接近與IDataReader,取列表的數據超過了DataTable。
- 支持多種數據庫。Dapper可以在所有Ado.net Providers下工作,包括sqlite, sqlce, firebird, oracle, MySQL, PostgreSQL and SQL Server
- 可以映射一對一,一對多,多對多等多種關系。
- 性能高。通過Emit反射IDataReader的序列隊列,來快速的得到和產生對象,性能不錯。
- 支持FrameWork2.0,3.0,3.5,4.0,4.5
在這里,我們在dotnet core下面使用Dapper操作MySQL。
首先,既然是演示,就先創建一個core MVC的項目,這里選擇有模板的。因為公司使用的是dotnet core2.2的版本,我就基於core2.2版本創建一個項目。

Dapper安裝,使用NuGet來安裝Dapper程序包

使用NuGet安裝MySQL.Data的程序包

安裝這些程序包之后,在appsettings.json文件中添加鏈接數據庫的字符串:
"ConnectionStrings": {
"DefaultConnection": "server=服務器;port=端口號;database=regatta{0};SslMode=None;uid=userName;pwd=passWord;Allow User Variables=true"
}
然后,封裝一個工具類,來獲得我們的連接字符串,和管理連接池。
public class BaseRepository : IDisposable
{
public static IConfigurationRoot Configuration { get; set; }
public static MySqlConnection conn;
public static MySqlConnection GetMySqlConnection(bool open = true,
bool convertZeroDatetime = false, bool allowZeroDatetime = false)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var cs = builder.GetSection("ConnectionStrings:DefaultConnection").Value;
var csb = new MySqlConnectionStringBuilder(cs)
{
AllowZeroDateTime = allowZeroDatetime,
ConvertZeroDateTime = convertZeroDatetime
};
conn = new MySqlConnection(csb.ConnectionString);
return conn;
}
public void Dispose()
{
if (conn != null && conn.State != System.Data.ConnectionState.Closed)
{
conn.Close();
}
}
}
或者, 在startup中注冊dapper倉儲,並現時注冊數據庫類型和數據庫連接串,因為在mysql和sqlserver中,它們的連接串是不同的,模塊化設計請看大叔這篇文章,《DotNetCore跨平台~組件化時代來了》
services.UseLog4Logger(o =>
{
o.Log4ConfigFileName = "log4.config";
o.ProjectName = "test";
}).UseDapper(o =>
{
o.ConnString = "server=服務器;port=端口號;database=regatta{0};SslMode=None;uid=userName;pwd=passWord;Allow User Variables=true";
o.DbType = DbType.MySql;
}).UseDefaultMQ();
創建數據庫中的一個需要映射的實體類:
public class Area
{
public int ID { get; set; }
public string Name { get; set; }
public int ParentID { get; set; }
}
當有些時候,數據庫中的表名,與我們定義的實體類的類名,可能會不一致。這個時候,就需要加一個特性標簽來聲明了:
[Display(Name = "tbl_area")]
public class Area
{
public int ID { get; set; }
public string Name { get; set; }
public int ParentID { get; set; }
}
當然,有些表中的字段與實體類中的自己定義的屬性,也不一樣,其實有好多解決方法,可以在使用T-SQL的時候,使用別名。比如,SELECT id AS ID FROM TABLE..,或者使用特性標簽,具體請參考:https://www.cnblogs.com/efreer/p/8277329.html
查詢操作
/// <summary>
/// 查詢單個數據
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
public static Area QueryFirstOrDefault(int Id)
{
var sql = "SELECT * from Area where id =@ID";
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.QueryFirstOrDefault<Area>(sql, new { ID = Id });
}
}
/// <summary>
/// 查詢所有數據
/// </summary>
/// <returns></returns>
public static List<Area> QueryList()
{
var sql = "select * from Area";
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.Query<Area>(sql).ToList();
}
}
/// <summary>
/// In操作
/// </summary>
public static List<Area> QueryIn()
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
var sql = "select * from Area where id in @ids";
//參數類型是Array的時候,dappper會自動將其轉化
return connection.Query<Area>(sql, new { ids = new int[2] { 1, 2 }, }).ToList();
}
}
public static List<Area> QueryIn(int[] ids)
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
var sql = "select * from Area where id in @ids";
//參數類型是Array的時候,dappper會自動將其轉化
return connection.Query<Area>(sql, new { ids }).ToList();
}
}
插入操作
/// <summary>
/// 插入一條數據
/// </summary>
/// <param name="Area"></param>
/// <returns></returns>
public static int Insert(Area Area)
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.Execute($"INSERT INTO Area(Name,ParentID) VALUES(@Name,@ParentID)", Area);
}
}
/// <summary>
/// 批量插入Area數據,返回影響行數
/// </summary>
/// <param name="Areas"></param>
/// <returns></returns>
public static int Insert(List<Area> Areas)
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.Execute("INSERT INTO Area(ID,Name,ParentID) VALUES(@ID,@Name,@ParentID)", Areas);
}
}
刪除操作
/// <summary>
/// 刪除一條
/// </summary>
/// <param name="Area"></param>
/// <returns></returns>
public static int Delete(Area Area)
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.Execute("DELETE FROM Area WHERE id=@ID", Area);
}
}
/// <summary>
/// 批量刪除
/// </summary>
/// <param name="Areas"></param>
/// <returns></returns>
public static int Delete(List<Area> Areas)
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.Execute("DELETE FROM Area WHERE id=@ID", Areas);
}
}
修改操作
/// <summary>
/// 修改
/// </summary>
/// <param name="Area"></param>
/// <returns></returns>
public static int Update(Area Area)
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.Execute("update Area set name=@name where id=@ID", Area);
}
}
/// <summary>
/// 批量修改
/// </summary>
/// <param name="Areas"></param>
/// <returns></returns>
public static int Update(List<Area> Areas)
{
using (IDbConnection connection = BaseRepository.GetMySqlConnection())
{
return connection.Execute("update Area set name=@name where id=@ID", Areas);
}
}
Join操作
我們是面向對象編程,所以一個對象里面會有許多其他子對象,這個子對象里面又有其自己的子對象,這種關系在數據庫里的表示就是外鍵。
比如我們有一本書book,它有主人person,book是一個對象,主人又是一個對象。
public class BookWithPerson
{
public int ID { get; set; }
public Person Pers { get; set; }
public string BookName { get; set; }
}
我們自然想要一個方法把數據庫里復雜的外鍵關系轉成我們需要的對象BookWithArea,所有我們需要的信息都存在里面,取數據的時候只要找這個對象取數據就行了,比如我們需要一本書的主人的姓名,我們只需要bookWithArea.Pers.Name。如果是一對多的關系我們用數組,如果是多對多我們加一層mapping。
現在我們想根據書的ID查詢書的信息,包括主人信息。那么
public static BookWithPerson QueryJoin(Book book)
{
using (IDbConnection connection = new SqlConnection(connectionString))
{
var sql = @"select b.id,b.bookName,p.id,p.name,p.remark
from Person as p
join Book as b
on p.id = b.personId
where b.id = @id;";
var result = connection.Query<BookWithPerson, Person, BookWithPerson>(sql,
(bookWithPerson, person) =>
{
bookWithPerson.Pers = person;
return bookWithPerson;
},
book);
//splitOn: "bookName");
return (BookWithPerson)result;
}
}
中,Query的三個泛型參數分別是委托回調類型1,委托回調類型2,返回類型。形參的三個參數分別是sql語句,map委托,對象參數。所以整句的意思是先根據sql語句查詢;同時把查詢的Area信息賦值給bookWithArea.Pers,並且返回bookWithArea;book是對象參數,提供參數綁定的值。
最終整個方法返回BookWithArea,這樣我們所需要的所有信息就有了。
這里只是簡單介紹下同步增刪改查的API的書寫,異步的API,可以自行去了解。

