關於orm框架設計,還有必要說的或許就是Model解析了,也是重要的一個環節,在實現上還是相對比較簡單的.
Model解析,主要用到的技術是反射了,即:把類的屬性與表的字段做映射. 把自己的設計及實現思路寫出來也希望能有人給很好的優化建議,同時也給新手一點啟發吧.
首先先給Model屬性定義特性,先普及一下"特性"的概念和為什麼用特性(Attribute).
簡單來說,特性是給一個類,或方法,或屬性 打上一個標記(或者叫附加信息),具體理解還是看例子比較好吧,
在做類與表之間映射時,我們需要知道某字段的是什麼類型,長度,是否主鍵,自增長,非空,等信息,最簡單較直觀的方法或許就是特性(Attribute)了,
首先我們定義一個特性,它就是一個類而已,它必須繼承自Attribute,我所寫的orm比較輕量級,僅幾個比較關鍵屬性,
代碼如下:

下面是一個實體類使用特性的例子,它指明了Id的列名是:"Id",不允許為空的,是自增長的,是主鍵:
public class Test1 : ModelBase
{
[ModelAttribute(IsPrimaryKey = true, IsIdentity = true, IsNotNull = false, ColumnName = "Id")]
public int Id { set; get; }
public string Name { set; get; }
public string Age { set; get; }
public string Remark { set; get; }
}
下面是通過反射把Model特性解析出來,先把核心代碼貼出來:
/// <summary>
/// 通過解析獲得Model的對象的參數,Key:為類的屬性名
/// </summary>
/// <param name="model">model對象</param>
/// <returns>返回model參數</returns>
protected override Dictionary<string, ModelAttribute> GetModelParam<TModel>()
{
var list = new Dictionary<string, ModelAttribute>();
PropertyInfo[] pros = ReflectionHelper.GetPropertyInfo<TModel>();
foreach (PropertyInfo item in pros)
{
var attr = ReflectionHelper.GetCustomAttribute<ModelAttribute>(item);
if (attr == null)
{
//如果實體沒定義屬性則創建一個新的
attr = new ModelAttribute();
attr.ColumnName = item.Name;
}
else
{
//如果列名沒有賦值,則將列名定義和屬性名一樣的值
if (string.IsNullOrEmpty(attr.ColumnName))
{
attr.ColumnName = item.Name;
}
}
list.Add(item.Name, attr);
}
return list;
}
因考慮反射應該是共同方法,不僅限於Model解析,所以把反射相關的方法提出來了,以下是根據"類型T"獲取自定義屬性的兩個方法:


解析特性我們不需要知道該類的具體實例,所以這裡用了泛型,只需要知道Model類型即可,我的框架僅限於類的屬性,這裡只獲取屬性的"特性對象".
返回類型Dictionary<string,ModelAttribute> Key:為屬性名,ModelAttribute 對象,
到這裡解析的實現其實就完成,後面我又做了一些優化,我們想到反射時通常會聯想到效率問題,而且既然是解析一個類的特性,那麼我們並不關心它的實例對象,
這裡把解析出來的對象放到了緩存,即:只有第一次對該類進行反射,以後都是直接訪問緩存數據.
解析Model是一個類,那麼需要做到全局緩存,我這裡用到了一個靜態變量,該變量是不允許被外部更改的,所以設置為私有的了.
代碼如下:
static object _LockObj1 = new object();
static object _LockObj2 = new object();
/// <summary>
/// 實體類緩存,靜態變量是保存為了減少反射次數
/// </summary>
static Dictionary<Type, Dictionary<string, ModelAttribute>> _ModelAttributeCache;
/// <summary>
/// 實體類緩存,靜態變量是保存為了減少反射次數
/// </summary>
protected Dictionary<Type, Dictionary<string, ModelAttribute>> ModelAttributeCache
{
get
{
if (_ModelAttributeCache == null)
{
lock (_LockObj1)
{
if (_ModelAttributeCache == null)
{
_ModelAttributeCache = new Dictionary<Type, Dictionary<string, ModelAttribute>>();
}
}
}
return _ModelAttributeCache;
}
}
/// <summary>
/// 獲取Model的屬性對象,獲取第一次後會放入一個緩存列表中
/// 即只反射一次
/// </summary>
public Dictionary<string, ModelAttribute> GetModelAttribute<T>() where T : ModelBase, new()
{
Type t = typeof(T);
if (!ModelAttributeCache.ContainsKey(t))
{
lock (_LockObj2)
{
if (!ModelAttributeCache.ContainsKey(t))
{
var attrs = GetModelParam<T>();
ModelAttributeCache.Add(t, attrs);
}
}
}
return ModelAttributeCache[t];
}
這裡緩存列表為: Dictionary<Type, Dictionary<string, ModelAttribute>> ,Type即Model類的類型.
解釋一下加LockObj的意義,
我先聲明一下,這個orm框架雖然比較輕量級,但我也不是共享的一個設計階段或者或測試階段的代碼,也是經過幾個小項目使用磨合過的.
_LockObj 是在一次多線程操作時發現的bug,當多個線程訪問一個"全局對象"時,不加鎖會訪問沖突的問題.
Model解析類的路徑:ZhCun.Framework.Common.Models.TableModel
下載了代碼的可以去看下具體實現的詳細方法.
在設計DalBase 時考慮了它應依賴抽象的理念,雖然沒有想好關於Model解析除了反射還是否會有其它方法,但還是把它定義成了抽象.
到這已經完成了Model解析的功能.會再生成sql語句的時候用到它.
有了以下方法示例,估計sql文的生成就能實現了吧.
//得到Model對象(第一次會反射,再次調用時是從緩存獲取)
Dictionary<string, ModelAttribute> modelAttr = _ModelAnaly.GetModelAttribute<T>();
//key:字段名(屬性名)
foreach (string item in modelAttr.Keys)
{
//得到列名(如果特性沒有指定ColumnName值,則與屬性名一樣)
string colName = modelAttr[item].ColumnName;
//是否字增長
bool isIdentity = modelAttr[item].IsIdentity;
//是否主鍵
bool isPrimaryKey = modelAttr[item].IsPrimaryKey;
}
關於Model解析類的實現 相對設計來說比較簡單.
如果有大神有啥好的建議,或有什麼不足,希望能 探討,指正 .