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