轉:C#制作ORM映射學習筆記一 自定義Attribute類
之前在做unity項目時發現只能用odbc連接數據庫,感覺非常的麻煩,因為之前做web開發的時候用慣了ORM映射,所以我想在unity中也用一下ORM(雖然我知道出於性能的考慮這樣做事不好的,不過自己的小項目嗎管他的,自己爽就行了)。不過現在世面上的ORM映射基本都是為web項目設計的,與unity項目很難契合,所以我決定自己做一個簡易的ORM映射。雖然想的很美但是實際做起來才發現還是挺復雜的,所以我在這裏記錄一下這次的開發過程,防止以後遺忘。
今天先記錄一下如何通過自定義attribute類實現對類名、屬性名和關系數據庫中的表名、字段名等信息映射。關於attribute類網上資料很多,這裏不詳細介紹了,下面具體代碼中用到的地方會有具體說明。
首先需要自定義三個attribute類,分別是TableAttribute、ColumnAttribute和PrimaryKeyAttribute,這三個類將分別描述表名、字段名和主鍵名。下面是具體的實現。
1.TableAttribute
using System; namespace ORM { [AttributeUsage(AttributeTargets.Class)] public class TableAttribute : Attribute { public TableAttribute(string tableName) {this.Value = tableName; } public string Value { get; protected set; } } }
2.ColumnAttribute
using System; namespace ORM { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class ColumnAttribute : Attribute {public ColumnAttribute(string columnName) { this.Value = columnName; } public string Value { get; protected set; } } }
3.PrimaryKeyAttribute
using System; namespace ORM { [AttributeUsage(AttributeTargets.Class)] public class PrimaryKeyAttribute : Attribute { public PrimaryKeyAttribute(string primaryKey) { this.Value = primaryKey; } public string Value { get; protected set; } public bool autoIncrement = false; } }
這裏要註意的地方不多,主要有以下幾點:
1.AttributeTargets是用來表名attribute類應該在何種程序實體前放置,class表示應該在類聲明前放置,Field表示可以在字段前放置,Property表示可以在屬性前放置。
2.AllowMultiple表示同一個程序體前能否放置多個相同的該自定義attribute類,這裏我設為false,因為一個屬性在數據表中只能對應一個字段。
3.Inherited表示在描述類屬性時,這個attribute能否被子類繼承,這裏我也設為了false,因為orm映射的類不會涉及到繼承的問題。
4.自定義的attribute在定義是類名都是以attribute結尾的,但是在使用時不需要將attribute也打出來,下面我舉個例子來說明。我用sqlite定義了一個userinfo表(為什麽是用sqlite原因很簡單,因為sqlite比較簡單粗暴),表結構如下。
這張表對應的類聲明如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ORM { [Table("userinfo")] [PrimaryKey("Id", autoIncrement = true)] public class User { [Column("Id")] public int Id { get; set; } [Column("UserName")] public string UserName { get; set; } [Column("Password")] public string Password { get; set; } [Column("CreateTime")] public DateTime CreateTime { get; set; } [Column("Status")] public bool Status { get; set; } [Column("RoleType")] public RoleType RoleType { get; set; } } public enum RoleType : int { Common = 1, Admin = 2 } }
為了在後面實現數據庫訪問,包括增刪改查操作時更加的方便,我們在做一個幫助類AttributeProcess,這個類是一個靜態類,裏面的方法也是靜態方法。設計這個類的目的是提供一個公共的方法來提取類所對應的表名、字段名、主鍵名的屬性。代碼如下:
using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Reflection; namespace ORM { public static class AttributeProcess { /// <summary> /// 獲取表名 /// </summary> /// <param name="type"></param> /// <returns></returns> public static string GetTableName(Type type) { string tableName = string.Empty; object[] attributes = type.GetCustomAttributes(false); foreach (var attr in attributes) { if (attr is TableAttribute) { TableAttribute tableAttribute = attr as TableAttribute; tableName = tableAttribute.Value; } } if (string.IsNullOrEmpty(tableName)) { tableName = type.Name; } return tableName; } /// <summary> /// 獲取字段名 /// </summary> /// <param name="property"></param> /// <returns></returns> public static string GetColumnName(PropertyInfo property) { string columnName = string.Empty; object[] attributes = property.GetCustomAttributes(false); foreach (var attr in attributes) { if (attr is ColumnAttribute) { ColumnAttribute columnAttr = attr as ColumnAttribute; columnName = columnAttr.Value; } } if (string.IsNullOrEmpty(columnName)) { columnName = property.Name; } return columnName; } /// <summary> /// 判斷主鍵是否自增 /// </summary> /// <param name="property"></param> /// <returns></returns> public static bool IsIncrement(Type type) { object[] attributes = type.GetCustomAttributes(false); foreach (var attr in attributes) { if (attr is PrimaryKeyAttribute) { PrimaryKeyAttribute primaryKeyAttr = attr as PrimaryKeyAttribute; return primaryKeyAttr.autoIncrement; } } return false; } /// <summary> /// 獲取主鍵名 /// </summary> /// <param name="type"></param> /// <returns></returns> public static string GetPrimary(Type type) { object[] attributes = type.GetCustomAttributes(false); foreach (var attr in attributes) { if (attr is PrimaryKeyAttribute) { PrimaryKeyAttribute primaryKeyAttr = attr as PrimaryKeyAttribute; return primaryKeyAttr.Value; } } return null; } /// <summary> /// 判斷屬性是否為主鍵 /// </summary> /// <param name="type"></param> /// <param name="property"></param> /// <returns></returns> public static bool IsPrimary(Type type, PropertyInfo property) { string primaryKeyName = GetPrimary(type); string columnName = GetColumnName(property); return (primaryKeyName == columnName); } } }
其中獲取自定義attribute和其中的屬性值的方法不難,主要就是先通過GetCustomAttributes方法來獲取程序體前放置的所有的自定義attribute,然後循環遍歷找到需要的attribute並讀取需要的屬性值,這樣就可以獲取到需要的數據庫相關信息了。另外,為了方便起見,在獲取表名和字段名時,如果沒有在類名或者屬性名前放置TableAttribute類或者ColumnAttribute類,那麽將自動的讀取類名或者屬性名做為表名和字段名返回。下面做一個簡單的測試,將user類對應的表名和其中屬性對應的字段名都打印出來,測試代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace ORM { class Program { static void Main(string[] args) { Type type = typeof(User); PropertyInfo[] properties = type.GetProperties(); Console.WriteLine(AttributeProcess.GetTableName(type)); foreach (var item in properties) { Console.WriteLine(AttributeProcess.GetColumnName(item)); } } } }
註:GetProperties方法是Type類下的一個方法,用來獲取類中的所有屬性的信息。
測試結果如下:
好了,到這裏自定義Attribute類的工作就基本完成了,下面就要正式開始正式的數據庫操作了,我會在後續的文章中進行說明,今天就先到這裏。
轉:C#制作ORM映射學習筆記一 自定義Attribute類