0. 前言
在之前的幾篇內容中,我們了解了如何通過ADO.NET 訪問資料庫,如何修改、新增資料,如何通過DataSet和DataAdapter獲取資料,我們將在這一篇試試自己實作一個簡單的ORM框架或者說ORM工具類,
涉及到的知識點:
- 反射(初級)
- ADO.NET 已有知識
1. ORM
那么,問題來了,什么是ORM?ORM全稱 Object Relational Mapping,翻譯過來就是物件關系映射,是一種通過描述物件與資料庫之間映射關系的資料,將物件保存到資料庫中的技術,
在C#中,曾經Entity Framework光芒萬丈,遮蓋了其他ORM框架的光輝(甚至如今都是如此),
后來慢慢涌現除了其他的一些ORM框架,進一步豐富了市場,所以現有比較流行的大概有以下幾種:
- Dapper 一個輕量的ORM框架
- Entity Framework/Entity Framework Core 功能完備的框架
- Nhibernate Java平臺上著名的Hibernate的.net版
- 等等
嗯,這是我最近找到的創作組還在更新的幾個框架,當然還有其他的很多有趣好用的ORM框架,歡迎各位補充哈,
這一篇的主要目的不是介紹這些框架(這是以后的內容),而是通過我們自己實作一個類ORM框架來了解底層核心,
2. 設計
我們先分析一下,如果我們設計一個物體物件與資料庫之間轉換的工具類應該具有哪些功能?
- 一個屬性與資料庫欄位的映射關系
- 增刪改查的SQL模板
- 查詢結果與物件的轉換
3. 實作
首先,宣告一個類,因為不能僅支持一種型別,所以這個類的所有與資料庫有關的方法都是泛型方法,或者這個類是泛型類,所以定義為泛型類:
public class OrmUtil<T>
{
}
我們事先約定類名即表名,屬性名即表的列名,所以我們可以快速得到以下內容:
/// <summary>
/// T的型別實體
/// </summary>
private Type dType;
/// <summary>
/// T的屬性表
/// </summary>
private PropertyInfo[] properties;
public OrmUtil()
{
dType = typeof(T);
properties = dType.GetProperties();
}
宣告一個資料庫連接:
public SqlConnection Connection { get; set; }
創建一個私有方法,檢查連接是否可用:
/// <summary>
/// 檢查連接是否可用
/// </summary>
/// <returns></returns>
private bool CheckConnection()
{
return Connection?.State == ConnectionState.Open;
}
準備作業完成,然后開始撰寫具體的業務方法:
Insert:
public int Insert(T entity)
{
if (!CheckConnection()) return -1;// 檢查狀態
var insert = $"insert into {dType.Name}({string.Join(",", properties.Select(t => t.Name))})";
var values = properties.Select(p => p.GetValue(entity));
var commandText = $"{insert} values('{string.Join("','", values)}')";
var command = Connection.CreateCommand();
command.CommandText = commandText;
var result = command.ExecuteNonQuery();
return result;
}
首先按照屬性名與列名之間的映射拼接 SQL,然后執行SQL命令,
Update:
public int Update(T entity,string keyName,object keyValue)
{
if (!CheckConnection()) return -1;
var setValues = properties.ToDictionary(p => p.Name, p => $"'{p.GetValue(entity)}'");
var setSql = string.Join(",", setValues.Select(pair=>$"{pair.Key}='{pair.Value}'"));
var sql = $"update {dType.Name} set {setSql} where {keyName} = '{keyValue}'";
var command = Connection.CreateCommand();
command.CommandText = sql;
return command.ExecuteNonQuery();
}
Update需要注意的就是如何正確拼接賦值sql,
Delete:
洗掉滿足條件的物件:
public int Delete(T entity)
{
if (!CheckConnection()) return -1;
var querySet = properties.Select(p => $"{p.Name} = '{p.GetValue(entity)}'");
var sql = $"delete from {dType.Name} where {string.Join(" and ", querySet)}";
var command = Connection.CreateCommand();
command.CommandText = sql;
return command.ExecuteNonQuery();
}
這里寫法有時候根據實際業務不同,大多數情況下洗掉主鍵對應的元素,或者滿足某一個條件的所有元素,這里只是做了個演示,小伙伴們可以試試自己改造一下,
Search:
先創建一個從DataTable轉成物件的工具方法:
private List<T> Convert(DataTable table)
{
var list = new List<T>(table.Rows.Count);//事先宣告一下容量
foreach(DataRow row in table.AsEnumerable())
{
T entity = Activator.CreateInstance<T>();
foreach(var p in properties)
{
if (!table.Columns.Contains(p.Name)) continue;// 如果屬性名不在表格中,則忽略
p.SetValue(entity, row[p.Name]);
}
list.Add(entity);
}
return list;
}
好,我們寫一個查詢方法:
public List<T> SearchAll()
{
var adapter = new SqlDataAdapter($"select * from {dType.Name}", Connection);
var set = new DataSet();
adapter.Fill(set);
return Convert(set.Tables[0]);
}
這樣一個簡單的ORM框架就這樣形成雛形了,當然實際上的ORM底層比這復雜,因為需要支持不同的資料庫,所以Connection 就不能簡簡單單的是一個SqlConnection了,或者底層不是像我們一樣取巧使用DataTable了,
實際上的DataTable到類物件的轉換要比我寫的復雜一點,因為還要判斷這個屬性是否是可讀、可寫的,
4. 總結
在這里我做了個拋磚引玉,帶領小伙伴們一起構思了一個簡陋的ORM框架,也讓大伙對此有了一定的印象,嗯,今天就到這了,同時ADO.NET 也告一段落了,接下來就是上Entity Framework了,當然,DataSet、DataAdapter這兩個類并沒有講完,這部分內容可能會在后續的番外篇內補全,
更多內容煩請關注我的博客《高先生小屋》

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/40661.html
標籤:C#
上一篇:C#實作FTP上傳資料
