關於Dapper的介紹,我想很多人都對它有一定的瞭解,這個類似一個輕型的ORM框架是目前應用非常火的一個東西,據說各方面的性能都不錯,而且可以支持多種資料庫,在開始介紹這個文章之前,我花了不少功夫來學習了Dapper 的相關使用。Dapper.Contrib是對Dapper的進一步封裝,使對象的基本... ...
關於Dapper的介紹,我想很多人都對它有一定的瞭解,這個類似一個輕型的ORM框架是目前應用非常火的一個東西,據說各方面的性能都不錯,而且可以支持多種資料庫,在開始介紹這個文章之前,我花了不少功夫來學習了Dapper 的相關使用。Dapper.Contrib是對Dapper的進一步封裝,使對象的基本增刪改查等操作進一步簡化,我做了一個案例使用Dapper.Contrib 開發.net core程式,測試它對多種資料庫的處理。
1、Dapper.Contrib的使用
前面介紹過,Dapper.Contrib是對Dapper的進一步封裝,使對象的基本增刪改查等操作進一步簡化。
它主要是通過特性映射的方式實現自定義類和資料庫之間的關係處理,如下是實體類的定義信息。
[Table("T_Customer")] public class CustomerInfo { [ExplicitKey]//非自增長的用此標識 public virtual string ID { get; set; } public virtual string Name { get; set; } public virtual int Age { get; set; } public virtual string Creator { get; set; } public virtual DateTime CreateTime { get; set; } }
Dapper.Contrib的所有實體配置選項
- Table:指定實體對應地資料庫表名,如果類名和資料庫表名不同,需要設置(如案例所示)
- Key:指定此列為自動增長主鍵
- ExplicitKey:指定此列為非自動增長主鍵(例如guid,字元串列)
- Computed:計算屬性,此列不作為更新
- Write:指定列是否可寫
通過定義好實體類和資料庫表的映射關係,就可以通過強類型處理相關的介面了,如下所示。
T Get<T>(id); IEnumerable<T> GetAll<T>(); int Insert<T>(T obj); int Insert<T>(Enumerable<T> list); bool Update<T>(T obj); bool Update<T>(Enumerable<T> list); bool Delete<T>(T obj); bool Delete<T>(Enumerable<T> list); bool DeleteAll<T>();
這樣通過映射指定表名或者欄位信息後,就可以知道類和表之間的關係,可以封裝對應的強類型處理介面了。
2、Dapper.Contrib 開發.net core程式
我們創建一個空白的.net core程式框架後,就在它的基礎上做一些Dapper的資料庫測試。
首先為了考慮多資料庫的處理,我們需要創建一個配置文件,並可以動態配置不同的資料庫,配置文件appSettings.json如下所示。
上面我配置了多種資料庫的連接字元串,並且通過動態指定節點名稱和資料庫類型,來實現對項目指向不同資料庫的訪問。
例如我們準備需要讓Dapper支持我們常見的資料庫類型,如下定義資料庫類型。
/// <summary> /// 資料庫類型定義 /// </summary> public enum DatabaseType { SqlServer, //SQLServer資料庫 MySql, //Mysql資料庫 Npgsql, //PostgreSQL資料庫 Oracle, //Oracle資料庫 Sqlite, //SQLite資料庫 DB2 //IBM DB2資料庫 }
對於不同的資料庫信息,我們需要根據不同的配置連接字元串,並創建對應的資料庫連接對象供Dapper使用,如對於SQLServer的資料庫,那麼創建的是SqlConnection對象,對於Mysql,創建的是MySqlConnection連接對象,對於PostgreSQL對應的是NpgsqlConnection,以此類推。而Dapper則通過對連接對象的擴展實現了多種數據請求。
對於多資料庫的支持,我們需要統一解析配置內容appSetting.json的內容,並返回不同資料庫的連接對象,如下是連接工廠的統一處理方式,通過 CreateConnection() 返回配置的連接對象。
/// <summary> /// 資料庫連接輔助類 /// </summary> public class ConnectionFactory { /// <summary> /// 轉換資料庫類型 /// </summary> /// <param name="databaseType">資料庫類型</param> /// <returns></returns> private static DatabaseType GetDataBaseType(string databaseType) { DatabaseType returnValue = DatabaseType.SqlServer; foreach (DatabaseType dbType in Enum.GetValues(typeof(DatabaseType))) { if (dbType.ToString().Equals(databaseType, StringComparison.OrdinalIgnoreCase)) { returnValue = dbType; break; } } return returnValue; } /// <summary> /// 獲取資料庫連接 /// </summary> /// <returns></returns> public static IDbConnection CreateConnection() { IDbConnection connection = null; //獲取配置進行轉換 var type = AppConfig.GetConfig("ComponentDbType"); var dbType = GetDataBaseType(type); //DefaultDatabase 根據這個配置項獲取對應連接字元串 var database = AppConfig.GetConfig("DefaultDatabase"); if (string.IsNullOrEmpty(database)) { database = "sqlserver";//預設配置 } var strConn = AppConfig.Configuration.GetConnectionString(database); switch (dbType) { case DatabaseType.SqlServer: connection = new System.Data.SqlClient.SqlConnection(strConn); break; case DatabaseType.MySql: connection = new MySql.Data.MySqlClient.MySqlConnection(strConn); break; case DatabaseType.Npgsql: connection = new Npgsql.NpgsqlConnection(strConn); break; case DatabaseType.Sqlite: connection = new SQLiteConnection(strConn); break; case DatabaseType.Oracle: connection = new Oracle.ManagedDataAccess.Client.OracleConnection(strConn); //connection = new System.Data.OracleClient.OracleConnection(strConn); break; case DatabaseType.DB2: //connection = new System.Data.OleDb.OleDbConnection(strConn); break; } return connection; } }
有了資料庫對象工廠,我們的配置就可以動態化了。
下麵我們來看看,獲得這些連接對象後,如何通過Dapper.Contrib來獲取對應的對象了,下麵的類是常規的對資料庫信息的處理,包括常規的增刪改查等基礎介面。
/// <summary> /// 常規的數據訪問層 /// </summary> public class Customer { public IDbConnection Connection { get { var connection = ConnectionFactory.CreateConnection(); connection.Open(); return connection; } } public IEnumerable<CustomerInfo> GetAll() { using (IDbConnection dbConnection = Connection) { return dbConnection.GetAll<CustomerInfo>(); //return dbConnection.Query<CustomerInfo>("SELECT * FROM T_Customer"); } } public CustomerInfo FindByID(string id) { using (IDbConnection dbConnection = Connection) { return dbConnection.Get<CustomerInfo>(id); //string query = "SELECT * FROM T_Customer WHERE ID = @Id"; //return dbConnection.Query<CustomerInfo>(query, new { Id = id }).FirstOrDefault(); } } public bool Insert(CustomerInfo info) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Insert(info) > 0; result = true; //string query = "INSERT INTO T_Customer (ID, Name, Age, Creator, CreateTime)" // + " VALUES(@ID, @Name, @Age, @Creator, @CreateTime)"; //result = dbConnection.Execute(query, info) > 0; } return result; } public bool Update(CustomerInfo prod) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Update(prod); //string query = "UPDATE T_Customer SET Name = @Name," // + " Age = @Age, Creator= @Creator, CreateTime=@CreateTime" // + " WHERE ID = @ID"; //result = dbConnection.Execute(query, prod) > 0; } return result; } public bool Delete(string id) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Delete(new CustomerInfo { ID = id }); //string query = "DELETE FROM T_Customer WHERE ID = @Id"; //result = dbConnection.Execute(query, new { ID = id }) > 0; } return result; } public bool DeleteAll() { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.DeleteAll<CustomerInfo>(); //string query = "DELETE FROM T_Customer WHERE ID = @Id"; //result = dbConnection.Execute(query, new { ID = id }) > 0; } return result; } }
其中的備註部分的代碼是等同於上面的執行代碼的,是Dapper 的SQL版本的一種處理方式。
我們看到,對於Customer表來說,使用對象的介面處理,我們已經隔離了很多硬編碼的SQL處理,不過我們還可以對它進行進一步的優化處理。
我們定義一個通用的BaseDAL來剝離常規的增刪改查處理,並且把同步和非同步的操作分來兩個文件來管理,同步處理的基類如下代碼所示。
/// <summary> /// 資料庫訪問基類 /// </summary> /// <typeparam name="T">實體類類型</typeparam> public partial class BaseDAL<T> where T : class { /// <summary> /// 對象的表名 /// </summary> public string TableName { get; set; } /// <summary> /// 主鍵屬性對象 /// </summary> public PropertyInfo PrimaryKey { get; set; } public BaseDAL() { this.TableName = EntityHelper.GetTableName(typeof(T)); this.PrimaryKey = EntityHelper.GetSingleKey<T>(); } /// <summary> /// 資料庫連接 /// </summary> protected IDbConnection Connection { get { var connection = ConnectionFactory.CreateConnection(); connection.Open(); return connection; } } /// <summary> /// 返回資料庫所有的對象集合 /// </summary> /// <returns></returns> public IEnumerable<T> GetAll() { using (IDbConnection dbConnection = Connection) { return dbConnection.GetAll<T>(); } } /// <summary> /// 查詢資料庫,返回指定ID的對象 /// </summary> /// <param name="id">主鍵的值</param> /// <returns></returns> public T FindByID(object id) { using (IDbConnection dbConnection = Connection) { return dbConnection.Get<T>(id); } } /// <summary> /// 插入指定對象到資料庫中 /// </summary> /// <param name="info">指定的對象</param> /// <returns></returns> public bool Insert(T info) { bool result = false; using (IDbConnection dbConnection = Connection) { dbConnection.Insert(info); result = true; } return result; } /// <summary> /// 插入指定對象集合到資料庫中 /// </summary> /// <param name="list">指定的對象集合</param> /// <returns></returns> public bool Insert(IEnumerable<T> list) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Insert(list) > 0; } return result; } /// <summary> /// 更新對象屬性到資料庫中 /// </summary> /// <param name="info">指定的對象</param> /// <returns></returns> public bool Update(T info) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Update(info); } return result; } /// <summary> /// 更新指定對象集合到資料庫中 /// </summary> /// <param name="list">指定的對象集合</param> /// <returns></returns> public bool Update(IEnumerable<T> list) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Update(list); } return result; } /// <summary> /// 從資料庫中刪除指定對象 /// </summary> /// <param name="info">指定的對象</param> /// <returns></returns> public bool Delete(T info) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Delete(info); } return result; } /// <summary> /// 從資料庫中刪除指定對象集合 /// </summary> /// <param name="list">指定的對象集合</param> /// <returns></returns> public bool Delete(IEnumerable<T> list) { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.Delete(list); } return result; } /// <summary> /// 根據指定對象的ID,從資料庫中刪除指定對象 /// </summary> /// <param name="id">對象的ID</param> /// <returns></returns> public bool Delete(object id) { bool result = false; using (IDbConnection dbConnection = Connection) { string query = string.Format("DELETE FROM {0} WHERE {1} = @id", TableName, PrimaryKey.Name); var parameters = new DynamicParameters(); parameters.Add("@id", id); result = dbConnection.Execute(query, parameters) > 0; } return result; } /// <summary> /// 從資料庫中刪除所有對象 /// </summary> /// <returns></returns> public bool DeleteAll() { bool result = false; using (IDbConnection dbConnection = Connection) { result = dbConnection.DeleteAll<T>(); } return result; } }
非同步類的代碼如下所示。
/// <summary> /// 資料庫訪問基類 /// </summary> /// <typeparam name="T">實體類類型</typeparam> public partial class BaseDAL<T> where T : class { /// <summary> /// 返回資料庫所有的對象集合 /// </summary> /// <returns></returns> public virtual async Task<IEnumerable<T>> GetAllAsync() { using (IDbConnection dbConnection = Connection) { return await dbConnection.GetAllAsync<T>(); } } /// <summary> /// 查詢資料庫,返回指定ID的對象 /// </summary> /// <param name="id">主鍵的值</param> /// <returns></returns> public virtual async Task<T> FindByIDAsync(object id) { using (IDbConnection dbConnection = Connection) { return await dbConnection.GetAsync<T>(id); } } /// <summary> /// 插入指定對象到資料庫中 /// </summary> /// <param name="info">指定的對象</param> /// <returns></returns> public virtual async Task<bool> InsertAsync(T info) { bool result = false; using (IDbConnection dbConnection = Connection) { await dbConnection.InsertAsync(info); result = true; } return await Task<bool>.FromResult(result); } /// <summary> /// 插入指定對象集合到資料庫中 /// </summary> /// <param name="list">指定的對象集合</param> /// <returns></returns> public virtual async Task<bool> InsertAsync(IEnumerable<T> list) { using (IDbConnection dbConnection = Connection) { return await dbConnection.InsertAsync(list) > 0; } } /// <summary> /// 更新對象屬性到資料庫中 /// </summary> /// <param name="info">指定的對象</param> /// <returns></returns> public virtual async Task<bool> UpdateAsync(T info) { using (IDbConnection dbConnection = Connection) { return await dbConnection.UpdateAsync(info); } } /// <summary> /// 更新指定對象集合到資料庫中 /// </summary> /// <param name="list">指定的對象集合</param> /// <returns></returns> public virtual async Task<bool> UpdateAsync(IEnumerable<T> list) { using (IDbConnection dbConnection = Connection) { return await dbConnection.UpdateAsync(list); } } /// <summary> /// 從資料庫中刪除指定對象 /// </summary> /// <param name="info">指定的對象</param> /// <returns></returns> public virtual async Task<bool> DeleteAsync(T info) { using (IDbConnection dbConnection = Connection) { return await dbConnection.DeleteAsync(info); } } /// <summary> /// 從資料庫中刪除指定對象集合 /// </summary> /// <param name="list">指定的對象集合</param> /// <returns></returns> public virtual async Task<bool> DeleteAsync(IEnumerable<T> list) { using (IDbConnection dbConnection = Connection) { return await dbConnection.DeleteAsync(list); } } /// <summary> /// 根據指定對象的ID,從資料庫中刪除指定對象 /// </summary> /// <param name="id">對象的ID</param> /// <returns></returns> public virtual async Task<bool> DeleteAsync(object id) { using (IDbConnection dbConnection = Connection) { string query = string.Format("DELETE FROM {0} WHERE {1} = @id", TableName, PrimaryKey.Name); var parameters = new DynamicParameters(); parameters.Add("@id", id); return await dbConnection.ExecuteAsync(query, parameters) > 0; } } /// <summary> /// 從資料庫中刪除所有對象 /// </summary> /// <returns></returns> public virtual async Task<bool> DeleteAllAsync() { using (IDbConnection dbConnection = Connection) { return await dbConnection.DeleteAllAsync<T>(); } } }
這樣,我們如果需要增加一個如客戶信息表的管理類,就很簡單的繼承基類就可以了,代碼很少,但是增刪改查介面一個也少不了。
/// <summary> /// 繼承基類對象管理 /// </summary> public class CustomerDAL :BaseDAL<CustomerInfo> { }
為了測試一下數據訪問層的處理介面,我創建了一個.net core的控制台程式進行測試,如下項目視圖所示。
主要目的是確認數據處理的效果。
我們在Program.cs類裡面增加相關的測試代碼,為了簡便和處理效果沒有用UnitTest處理。
//創建管理對象,並測試介面 var customer = new CustomerDAL(); var list = customer.GetAll(); foreach (var item in list) { Console.WriteLine(item.ToJson()); var info = customer.FindByID(item.ID); Console.WriteLine(info.ToJson()); Console.WriteLine(); } //插入記錄 var insertInfo = new CustomerInfo() { Name = "test", Age = 30, Creator = "test" }; var insertList = new List<CustomerInfo>() { insertInfo }; var flag = customer.Insert(insertList); Console.WriteLine("插入操作" + (flag ? "成功" : "失敗")); Console.WriteLine("插入的新內容"); insertInfo = customer.FindByID(insertInfo.ID); Console.WriteLine(insertInfo.ToJson()); Console.WriteLine("更新內容"); insertInfo.Name = "Test" + DateTime.Now.ToShortDateString(); flag = customer.Update(insertInfo); Console.WriteLine("更新操作" + (flag ? "成功" : "失敗")); Console.WriteLine("更新的新內容"); insertInfo = customer.FindByID(insertInfo.ID); Console.WriteLine(insertInfo.ToJson()); Console.WriteLine("刪除內容"); flag = customer.Delete(insertInfo.ID); Console.WriteLine("刪除操作" + (flag ? "成功" : "失敗")); Console.WriteLine("所有內容"); list = customer.GetAll(); foreach (var item in list) { Console.WriteLine(item.ToJson()); Console.WriteLine(); } Console.ReadLine();
測試Mysql、SQLite資料庫同樣沒有問題
Mysql配置信息如下
處理的Mysql記錄信息如下。
SQLite配置信息如下
處理SQLite數據信息如下
而在處理PostgreSQL的信息(配置節點npgsql裡面)的時候,查詢的主鍵好像和大小寫有關係,導致插入記錄出錯。
而Oracle我採用的是Oracle.ManagedDataAccess.Core進行訪問,由於我本地Oracle資料庫偵聽處理有點問題,因此沒有測試成功,暫不予置評。
而對於資料庫的支持問題,導致我重新審核一下是否採用Dapper.Contrib還是其他Dapper方式來構建資料庫訪問基類的問題,我需要相容多種資料庫的信息,並且能夠儘可能的封裝常規的增刪改查等操作,其中目前的基類還沒有加入更加複雜的查詢操作,分頁操作等功能,在解決這些困惑問題,才會繼續考慮把底層支持的介面全部完善。