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類的工作就基本完成了,下面就要正式開始正式的數據庫操作了,我會在後續的文章中進行說明,今天就先到這裏。



發佈了31 篇原創文章 · 獲贊 59 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章