對象關係映射,靠純手打,是個超級繁瑣又類的苦力活,即便它能給你再高的運行效率,但是也會損失開發效率。
c#在netframework中其實已經提供了ORM架構了,叫做entity framework,這個架構其實已經做的蠻好的了,但是架構實在是太重,重到寫出來的代碼在配置不那麼好的電腦上跑起來都卡頓。
因此我們決定自己寫這麼一個架構,一來重在輕量;二來,就是想探探路,看看ORM架構到底是怎麼做出來的。
當然,讓我自己憑空造樓是做不到的,但是我們可以借鑒,尤其是在互連網這麼發達的時代,去github看看別的的開原始碼就OK了。
一、反射。
首先我們要解決的就是當從資料庫中取出值來了之後,怎麼直接賦值給相應的對象的屬性的問題,我們記得,傳統的做法就是
dt.Rows[i][“ID”]
這麼一個一個欄位去寫,碰到欄位比較多的表,那就會打半天了。
反射,就是要獲得該類的變數的名稱,然後賦值給該變數:
| 代碼如下 |
複製代碼 |
public List<T> ExecSelect<T>(string sqlCmd) where T : new() { List<T> list = new List<T>(); DataTable dt = Select(sqlCmd); if (dt.Rows.Count == 0) { return list; } PropertyInfo[] properties = ReflectionHelper.GetProperties(new T().GetType()); for (int i=0;i<dt.Rows.Count;i++) { T entity = new T(); foreach (PropertyInfo property in properties) { String name = property.Name; ReflectionHelper.SetPropertyValue(entity, property, dt.Rows[i][name]); } list.Add(entity); } return list; } |
以上的T就是一種泛型,至於泛型是什麼,自行百度。
GetProperties函數即是泛型用於擷取類的變數的方法,它有一些參數,包括擷取變數為public修飾等等參數:
| 代碼如下 |
複製代碼 |
public static PropertyInfo[] GetProperties(Type type) { return type.GetProperties(BindingFlags.Public | BindingFlags.Instance); }
|
二、Value.
既然我們已經通過GetProperties擷取了該類的屬性,那麼通過SetValue直接給其賦值就好了,問題是,如果我們從資料庫中取出來的值和它不匹配呢,因此我們需要轉化Value類型。
| 代碼如下 |
複製代碼 |
if (Convert.IsDBNull(value) || (value == null)) { return null; } string typeName = type.FullName.ToString(); System.Console.WriteLine(typeName); if (type == typeof(System.Nullable<UInt16>)) { value = Convert.ToUInt16(value); } else if (type == typeof(System.Nullable<UInt32>)) { value = Convert.ToUInt32(value); } else if (type == typeof(System.Nullable<UInt64>)) { value = Convert.ToUInt64(value); } else if (type == typeof(System.Nullable<Int32>)) { value = Convert.ToInt32(value); } else if (type == typeof(System.Nullable<Int64>)) { value = Convert.ToInt64(value); } switch (typeName) { case "System.String": if (!isNullOrEmpty(value)) value = value.ToString(); break; case "System.Boolean": if (!isNullOrEmpty(value)) value = Convert.ToBoolean(value); break; case "System.Int16": if (!isNullOrEmpty(value)) value = Convert.ToInt16(value); break; case "System.Int32": if (!isNullOrEmpty(value)) value = Convert.ToInt32(value); break; case "System.Int64": if (!isNullOrEmpty(value)) value = Convert.ToInt64(value); break; case "System.Double": if (!isNullOrEmpty(value)) value = Convert.ToDouble(value); break; case "System.Float": if (!isNullOrEmpty(value)) value = Convert.ToDouble(value); break; case "System.Single": if (!isNullOrEmpty(value)) value = Convert.ToDouble(value); break; case "System.Decimal": if (!isNullOrEmpty(value)) value = Convert.ToDecimal(value); break; case "System.DateTime": if (!isNullOrEmpty(value)) value = Convert.ToDateTime(value); break; } return value; } |
三、反射與動態編譯。
Reflection真的是個好東西,但是net4.0後會有一個更好的東西:dynamic,要知道反射其實是很費效率的,但是動態編譯就會快的多,如果你不理解dynamic,那麼以下的例子就會告訴你這兩者的區別:
public void test()
{
SomeClass c = new SomeClass();
PropertyInfo property = c.GetType().GetProperties()[0];
StartTest("begin reflection.");
for (int i = 0; i < 1000000; i++)
{
property.SetValue(c, i, null);
}
EndTest("end reflection");
IDynamicPropertyInfo dproperty = new DynamicType(c.GetType()).GetProperties()[0];
StartTest("begin dynamic.");
for (int i = 0; i < 1000000; i++)
{
dproperty.SetValue(c, i, null);
}
EndTest("end dynamic");
}
那麼這兩者的時間差呢:
—— Test started: Assembly: Pixysoft.Framework.Reflection.dll ——
begin reflection.
end reflection
00:00:09.0625000
begin dynamic.
end dynamic
00:00:00.0468750
差別就太遠了,所以如果你覺得因為反射你的程式變慢了,不如試試動態編譯。