1. 程式人生 > >轉:C#制作ORM映射學習筆記一 自定義Attribute類

轉:C#制作ORM映射學習筆記一 自定義Attribute類

技術 sage 其中 username pac ont 學習 collect reat

之前在做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類