對象關係映射,靠純手打,是個超級繁瑣又類的苦力活,即便它能給你再高的運行效率,但是也會損失開發效率。
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
差別就太遠了,所以如果你覺得因為反射你的程式變慢了,不如試試動態編譯。