C#製作ORM對映學習筆記一 自定義Attribute類
阿新 • • 發佈:2019-01-29
之前在做unity專案時發現只能用odbc連線資料庫,感覺非常的麻煩,因為之前做web開發的時候用慣了ORM對映,所以我想在unity中也用一下ORM(雖然我知道出於效能的考慮這樣做事不好的,不過自己的小專案嗎管他的,自己爽就行了)。不過現在世面上的ORM對映基本都是為web專案設計的,與unity專案很難契合,所以我決定自己做一個簡易的ORM對映。雖然想的很美但是實際做起來才發現還是挺複雜的,所以我在這裡記錄一下這次的開發過程,防止以後遺忘。
今天先記錄一下如何通過自定義attribute類實現對類名、屬性名和關係資料庫中的表名、欄位名等資訊對映。關於attribute類網上資料很多,這裡不詳細介紹了,下面具體程式碼中用到的地方會有具體說明。
首先需要自定義三個attribute類,分別是TableAttribute、ColumnAttribute和PrimaryKeyAttribute,這三個類將分別描述表名、欄位名和主鍵名。下面是具體的實現。
1.TableAttribute
2.ColumnAttributeusing System; namespace ORM { [AttributeUsage(AttributeTargets.Class)] public class TableAttribute : Attribute { public TableAttribute(string tableName) { this.Value = tableName; } public string Value { get; protected set; } } }
3.PrimaryKeyAttributeusing 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; } } }
這裡要注意的地方不多,主要有以下幾點: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類的工作就基本完成了,下面就要正式開始正式的資料庫操作了,我會在後續的文章中進行說明,今天就先到這裡。