1. 程式人生 > >C#製作ORM對映學習筆記一 自定義Attribute類

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