DataTable 深入解析数据源绑定原理之高级篇
前言
在上篇写了篇 实战系列之天气预报实时采集 ,有个别同志认为没技术含量,也许正如所说。
1:如果你还不懂,请看写法,了解想法。
2:如果你已懂,略过写法,请看想法。
其实纵观我一直写来的200多篇文章,基本都可以看出那么点痕迹:
二:没有华丽理论型的文章。
三:实战型文章很多。
四:文章尽量面向新手的表述,尽量了。
一、Winform下的DataGridView不支持使用DataReader绑定
1:问题产生
2:思考分析试验
因此,做个试验:使用SqlDataReader直接绑定Winform下的DataGridView,发现失败了。
于是大量搜索,发现DataReader实在无法直接绑定DataGridView,通过数据源控件中转绑定的就算了。
3:得出结论
只好另寻方法-》DataGridView支持DataTable,于是要从DataTable入手了。
二、DataTable很强大,支持Web又支持Winform
1:分析绑定原理
一种是实现IEnumerable接口,即当初走的DataReader方式实现的绑定。
另一种是实现IListSource接口,即走DataTable方式实现的绑定。
为啥当初不实现DataTable方式的绑定,不就完了,两种都支持~~-_-..现在又得回去折腾IListSource接口的实现。
2:深入DataTable绑定原理
我们通过Reflector反编绎看下DataTable继承实现的接口:
几乎都是我们平常没用到的接口,不理先,我们关注IListSource怎么实现绑定的。如果自己看一下IListSource要实现的接口有几个方法:
{
// Methods
IList GetList();
// Properties
bool ContainsListCollection { get; }
}
就两个,太容易了,接着我们要在DataTable 6000多行的代码中找到IListSource的实现,查找是最好的方法:
bool IListSource.ContainsListCollection
{
get { return false; }
IList IListSource.GetList()
{
return this.DefaultView;
}
GetList接口没事就返回了个默认视图,又要切进去看视图了。
{
get
{
DataView defaultView = this.defaultView;
if (defaultView == null)
{
if (this.dataSet != null)
{
defaultView = this.dataSet.DefaultViewManager.CreateDataView(this);
}
else
{
defaultView = new DataView(this, true);
defaultView.SetIndex2("", DataViewRowState.CurrentRows, null, true);
}
defaultView = Interlocked.CompareExchange<DataView>(ref this.defaultView, defaultView, null);
if (defaultView == null)
{
defaultView = this.defaultView;
}
}
return defaultView;
}
}
切进去就一大堆,实在没心情看下去,省略中间看个头与尾,只知道返回了个DataView。
忽悠:
我只想知道IListSource怎么实现绑定,至于其它有一堆没一堆的我根本不关心,我只要我想要的。
扫了一眼接口,发现是继承了IList,这和IListSource要求的返回值IList是一致的。
神马啊神马,没点头绪,完全找不到绑定的重点,难道说,随便找个IList返回的类就行了?于是让MDataTable实现IListSource接口,试试看:
实现接口:
{
return Rows;
}
接着忽悠:
继承折腾DataView,传说DataView也能直接绑定控件的,yo~~有一丝想法。。
于是看一下其实现IList接口的源码,发现一堆都在操作DataRowView
没法忽悠了:
又是一堆很陌生的接口,于是到这里,我几乎停止了脚步,因为我分析不下去了~~。
上WC仔细从头想过:
对于IList<实体>绑定,所有的属性都会被认为是列名,其值为行的值。而对于DataTable,里面又是怎么认识出列名和分析出值的呢?
2:从DataRow中也看不到提取列名的方法,其关键性的IList接口的相关实现引出了->DataRowView。
3:DataRowView?是神秘的所在?一堆继承的接口也是很陌生。
回头继续搜索:
转换思路继续大量搜索:换了很多关键字,搜中文又搜E文。结果尽是一堆自定义控件开发的东东,结果印象中在某一篇的googleE文的“网页快照”中发现一段E文,原文不知是哪了,上次都记得只能打开快照,现在估计能快照都没了,按想象翻译出来的中文大致为:
偶滴神啊~能从千军万马的E文中,扫到几个关键字不容易啊!!!
只是遥想当年,我并不像现在写文这么冷静,我当初早把Reflector关掉了,哪还记得DataRowView实现了ICustomTypeDescriptor,
再说ICustomTypeDescriptor对我又是那么的陌生,是那么的陌生,...很陌生。。。
秘密已经出来了:
是它,就是它,就是它实现如何识别哪些是列名,哪些是列值。
3:浅入ICustomTypeDescriptor
如果你搜索此接口,你会发现一堆的文章都是说移动控件开发,我就是从移动控件开发中很辛苦的挖了点示例实现了。
不过此文就不走弯路了,直接分析DataRowView,对于 ICustomTypeDescriptor接口,有很多方法:
{
// Methods
AttributeCollection GetAttributes();
string GetClassName();
string GetComponentName();
TypeConverter GetConverter();
EventDescriptor GetDefaultEvent();
PropertyDescriptor GetDefaultProperty();
object GetEditor(Type editorBaseType);
EventDescriptorCollection GetEvents();
EventDescriptorCollection GetEvents(Attribute[] attributes);
PropertyDescriptorCollection GetProperties();
PropertyDescriptorCollection GetProperties(Attribute[] attributes);
object GetPropertyOwner(PropertyDescriptor pd);
}
不过基本是摆设,只因用不到,除了一个接口方法:GetProperties(Attribute[] attributes)
于是我们分析DataRowView对此接口的实现:
{
if (this.dataView.Table == null)
{
return zeroPropertyDescriptorCollection;
}
return this.dataView.Table.GetPropertyDescriptorCollection(attributes);
}
继续深入:
{
if (this.propertyDescriptorCollectionCache == null)
{
int count = this.Columns.Count;
int num4 = this.ChildRelations.Count;
PropertyDescriptor[] properties = new PropertyDescriptor[count + num4];
for (int i = 0; i < count; i++)
{
properties[i] = new DataColumnPropertyDescriptor(this.Columns[i]);
}
for (int j = 0; j < num4; j++)
{
properties[count + j] = new DataRelationPropertyDescriptor(this.ChildRelations[j]);
}
this.propertyDescriptorCollectionCache = new PropertyDescriptorCollection(properties);
}
return this.propertyDescriptorCollectionCache;
}
关键定位,只是返回一组:DataColumnPropertyDescriptor 。
那DataColumnPropertyDescriptor是什么?继续深入:
{
this.column = dataColumn;
}
两行代码,那个base是啥?是PropertyDescriptor ,实现很简单,把列名传过去就行了,至此,就结束了。不知道有多少会看到这里,估计本文大伙也就是扫下来,除非某天要应用到,不然只是忽悠下眼球了。
总结下具体实现ICustomTypeDescriptor接口方法:
2:重点实现GetProperties(Attribute[] attributes)方法。
3:需要自定义属性描述类,而这自定义的属性描述类需要继承自抽象基类PropertyDescriptor。
4:GetProperties返回的是自定义属性描述类的集合。
三、绑定原理分析完,MDataTable模仿出击
1:MDataTable继承IListSource接口实现
public bool ContainsListCollection
{
get
{
return true;
}
}
public IList GetList()
{
return Rows;
}
#endregion
2:MDataRow继承ICustomTypeDescriptor接口实现
A:先实现自定义属性描述类
{
private MDataCell cell = null;
public MDataProperty(MDataCell mdc, Attribute[] attrs)
: base(mdc._CellStruct.ColumnName, attrs)
{
cell = mdc;
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get
{
return typeof(MDataCell);
}
}
public override object GetValue(object component)
{
return ((MDataRow)component)[cell._CellStruct.ColumnName].Value;
}
public override bool IsReadOnly
{
get
{
return false;
}
}
public override Type PropertyType
{
get { return cell._CellStruct.ValueType; }
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
cell.Value = value;
}
public override bool ShouldSerializeValue(object component)
{
return true;
}
public override bool IsBrowsable
{
get
{
return true;
}
}
}
B:实现重点方法GetProperties(Attribute[] attributes)
PropertyDescriptorCollection properties;
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
if (index == 1)
{
return properties;
}
index++;
properties = new PropertyDescriptorCollection(null);
foreach (MDataCell mdc in this)
{
properties.Add(new MDataProperty(mdc, null));
}
return properties;
}
OK,此至,MDataTable顺利完成了对Winform下DataGridView的支持。本文原标题:CYQ.Data 轻量数据层之路 MDataTable绑定Winform之DataGridView 原理高级篇(三十一)
四、总结
不追了,MDataTable增加了ToJson方法和ToList<实体>方法,可直接用json传过去再用反json系列化解析成List<实体>型就可以直接绑定了。