以前喜歡DataTable,覺得它通用性強,只要一條SQL語句就可以自動對應出所有的列;相比之下,實體類必須提前建立好,映射的時候又太死,還繞了個圈不直接。
現在能力提高了,終於發現了實體類的優勢,那是DataTable遠遠不能相比的,只不過目前有一個自己寫的項目還沒完成,是基於DataTable的,還不能立刻使用實體類,真的可惜。
特別說明:我要說的實體類,絕對不是EF等現有的ORM架構,我根本沒看過那些架構,我僅從自己的經驗得出實體類的強勢,並加以改良,我敢說,現有的著名架構都沒達到我這個程度。
先說說DataTable的局限性,它的靈活性帶來的一個最大的問題就是:開發通用模組不方便。在資料庫編程中,通用模組往往需要強型別的傳遞,弱類型如果因為內部列有變化,會導致調用模組出錯,達不到預期效果。
再說說實體類的強勢,傳統的實體類是強型別的,但是有一種動態實體類的概念,讓實體類支援弱類型,這樣實體類就把DataTable所能處理的功能都包含了,剩下的事就是設計者怎麼設計這個動態實體類和強名實體類的結合使用了,現有ORM架構都不支援動態實體類,這也是我斷言我設計思路的先進性。
下面簡單說下動態實體類的技術點,靈感來自Dapper(僅限.NET4.0以上版本,低版本需要改造)
這個是Dapper中定義的一個動態類型,也可以使用微軟提供的ExpandoObject類,效果類似:
動態類 private class FastExpando : System.Dynamic.DynamicObject, IDictionary<string, object>
{
IDictionary<string, object> data;
public static FastExpando Attach(IDictionary<string, object> data)
{
return new FastExpando { data = data };
}
public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
{
data[binder.Name] = value;
return true;
}
public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
{
return data.TryGetValue(binder.Name, out result);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return data.Keys;
}
#region IDictionary<string,object> Members
void IDictionary<string, object>.Add(string key, object value)
{
throw new NotImplementedException();
}
bool IDictionary<string, object>.ContainsKey(string key)
{
return data.ContainsKey(key);
}
ICollection<string> IDictionary<string, object>.Keys
{
get { return data.Keys; }
}
bool IDictionary<string, object>.Remove(string key)
{
throw new NotImplementedException();
}
bool IDictionary<string, object>.TryGetValue(string key, out object value)
{
return data.TryGetValue(key, out value);
}
ICollection<object> IDictionary<string, object>.Values
{
get { return data.Values; }
}
object IDictionary<string, object>.this[string key]
{
get
{
return data[key];
}
set
{
if (!data.ContainsKey(key))
{
throw new NotImplementedException();
}
data[key] = value;
}
}
#endregion
#region ICollection<KeyValuePair<string,object>> Members
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
void ICollection<KeyValuePair<string, object>>.Clear()
{
throw new NotImplementedException();
}
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
return data.Contains(item);
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
data.CopyTo(array, arrayIndex);
}
int ICollection<KeyValuePair<string, object>>.Count
{
get { return data.Count; }
}
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
{
get { return true; }
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable<KeyValuePair<string,object>> Members
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
{
return data.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return data.GetEnumerator();
}
#endregion
}
這個動態類在.NET4.0裡面已經可以很好的工作了,用它動態載入資料,就是一個動態實體類。但是有一個缺陷,就是無法從表格控制項中載入這些資料,類似GridView的控制項,是通過反射來擷取類屬性的,但是動態類不支援反射內部屬性,因此Dapper只完成了一半工作,剩下的一半工作就需要自己來完成了。
給動態類添加反射支援:
自訂屬性描述類 class FastExpandoDescriptionProvider : TypeDescriptionProvider
{
public FastExpandoDescriptionProvider() : base() { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new FastExpandoCustomTypeDescriptor(objectType, instance);
}
}
class FastExpandoCustomTypeDescriptor : CustomTypeDescriptor
{
public FastExpandoCustomTypeDescriptor(Type objectType, object instance)
: base()
{
if (instance != null)
{
var tmp = (FastExpando)instance;
var names = tmp.GetDynamicMemberNames();
foreach (var name in names)
{
customFields.Add(new DynamicPropertyDescriptor(name, instance));
}
}
}
List<PropertyDescriptor> customFields = new List<PropertyDescriptor>();
public override PropertyDescriptorCollection GetProperties()
{
return new PropertyDescriptorCollection(customFields.ToArray());
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(customFields.ToArray());
}
}
class DynamicPropertyDescriptor : PropertyDescriptor
{
Type propertyType = typeof(object);
public DynamicPropertyDescriptor(string name, object instance)
: base(name, null)
{
var obj = (IDictionary<string, object>)instance;
propertyType = obj[name].GetType();
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get
{
return typeof(FastExpando);
}
}
public override object GetValue(object component)
{
IDictionary<string, object> obj = (IDictionary<string, object>)component;
return obj[Name];
}
public override bool IsReadOnly
{
get
{
return false;
}
}
public override Type PropertyType
{
get
{
return propertyType;
}
}
public override void ResetValue(object component)
{
throw new NotImplementedException();
}
public override void SetValue(object component, object value)
{
IDictionary<string, object> obj = (IDictionary<string, object>)component;
obj[Name] = value;
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
接下來只要在任何地方(建議在靜態建構函式中)添加如下代碼,這個動態類就可以成功給介面視圖控制項載入了:
FastExpandoDescriptionProvider provider = new FastExpandoDescriptionProvider();
TypeDescriptor.AddProvider(provider, typeof(FastExpando));
這樣結合強名實體類,就再有沒有使用DataTable的必要了。由於我目前還沒辦法完全扔掉DataTable,這個實體類的設計暫時擱下,構思已經全部好了,最終實現的實體類操作,高效且強大,前景誘人。