以前喜欢DataTable,觉得它通用性强,只要一条SQL语句就可以自动映射出所有的列;相比之下,实体类必须提前创建好,映射的时候又太死,还绕了个圈不直接。
现在能力提高了,终于发现了实体类的优势,那是DataTable远远不能相比的,只不过目前有一个自己写的项目还没完成,是基于DataTable的,还不能立刻使用实体类,真的可惜。
特别说明:我要说的实体类,绝对不是EF等现有的ORM框架,我根本没看过那些框架,我仅从自己的经验得出实体类的强势,并加以改良,我敢说,现有的著名框架都没达到我这个程度。
先说说DataTable的局限性,它的灵活性带来的一个最大的问题就是:开发通用模块不方便。在数据库编程中,通用模块往往需要强类型的传递,弱类型如果因为内部列有变化,会导致调用模块出错,达不到预期效果。
再说说实体类的强势,传统的实体类是强类型的,但是有一种动态实体类的概念,让实体类支持弱类型,这样实体类就把DataTable所能处理的功能都包含了,剩下的事就是设计者怎么设计这个动态实体类和强名实体类的结合使用了,现有ORM框架都不支持动态实体类,这也是我断言我设计思路的先进性。
下面简单说下动态实体类的技术点,灵感来自Dapper(仅限.NET4.0以上版本,低版本需要改造)
这个是Dapper中定义的一个动态类型,也可以使用微软提供的ExpandoObject类,效果类似:
{
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只完成了一半工作,剩下的一半工作就需要自己来完成了。
给动态类添加反射支持:
{
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;
}
}
接下来只要在任何地方(建议在静态构造函数中)添加如下代码,这个动态类就可以成功给界面视图控件加载了:
TypeDescriptor.AddProvider(provider, typeof(FastExpando));
这样结合强名实体类,就再有没有使用DataTable的必要了。由于我目前还没办法完全扔掉DataTable,这个实体类的设计暂时搁下,构思已经全部好了,最终实现的实体类操作,高效且强大,前景诱人。