一起谈.NET技术,DataTable 深入解析数据源绑定原理之高级篇

  前言

  在上篇写了篇 实战系列之天气预报实时采集 ,有个别同志认为没技术含量,也许正如所说。

只是人各有看法,当我写出一篇文章时,我只是希望:
1:如果你还不懂,请看写法,了解想法。
2:如果你已懂,略过写法,请看想法。

  其实纵观我一直写来的200多篇文章,基本都可以看出那么点痕迹:

一:没有水文。
二:没有华丽理论型的文章。
三:实战型文章很多。
四:文章尽量面向新手的表述,尽量了。

  一、Winform下的DataGridView不支持使用DataReader绑定

  1:问题产生

在 CYQ.Data 框架 进行到V1.5版本要支持Winform时,曾遇到一个问题,就是无法绑定DataGridView。

  2:思考分析试验

MDataTable走的是DataReader方式实现的绑定,除非DataReader无法绑定DataGridView,不然就是自己实现有问题。
因此,做个试验:使用SqlDataReader直接绑定Winform下的DataGridView,发现失败了。
于是大量搜索,发现DataReader实在无法直接绑定DataGridView,通过数据源控件中转绑定的就算了。

  3:得出结论

DataReader方式都无法绑定Winform下的DataGridView,我这继承之DataReader的实现方式也就更无从实现绑定了。
只好另寻方法-》DataGridView支持DataTable,于是要从DataTable入手了。

  二、DataTable很强大,支持Web又支持Winform

  1:分析绑定原理

在以前的MDataTable实现绑定原理篇中,我们研究出要实现绑定,有两种方式:
一种是实现IEnumerable接口,即当初走的DataReader方式实现的绑定。
另一种是实现IListSource接口,即走DataTable方式实现的绑定。
为啥当初不实现DataTable方式的绑定,不就完了,两种都支持~~-_-..现在又得回去折腾IListSource接口的实现。

  2:深入DataTable绑定原理

  我们通过Reflector反编绎看下DataTable继承实现的接口:

public class DataTable : MarshalByValueComponent, IListSource, ISupportInitializeNotification, ISupportInitialize, ISerializable, IXmlSerializable

  几乎都是我们平常没用到的接口,不理先,我们关注IListSource怎么实现绑定的。如果自己看一下IListSource要实现的接口有几个方法:

public interface IListSource
{
    // Methods
    IList GetList();
    // Properties
    bool ContainsListCollection { get; }
}

  就两个,太容易了,接着我们要在DataTable 6000多行的代码中找到IListSource的实现,查找是最好的方法:

//DataTable的实现
bool IListSource.ContainsListCollection
{
    get {  return false; }

}

IList IListSource.GetList()
{
    return this.DefaultView;
}

  GetList接口没事就返回了个默认视图,又要切进去看视图了。

public DataView DefaultView
{
    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。

public class DataView : MarshalByValueComponent, IBindingListView, IBindingList, IList, ICollection, IEnumerable, ITypedList, ISupportInitializeNotification, ISupportInitialize

  忽悠:

又是神马般的一堆接口,内部代码太多,实在没心情看;
我只想知道IListSource怎么实现绑定,至于其它有一堆没一堆的我根本不关心,我只要我想要的。
扫了一眼接口,发现是继承了IList,这和IListSource要求的返回值IList是一致的。

  神马啊神马,没点头绪,完全找不到绑定的重点,难道说,随便找个IList返回的类就行了?于是让MDataTable实现IListSource接口,试试看:

public class MDataTable : IDataReader, IEnumerable,System.ComponentModel.IListSource

  实现接口:

public IList GetList()
{
    return Rows;
}

  接着忽悠:

好说我的Rows也是继承自List<xxx>的,试着绑定~~结果很飘逸,出来完全不是我想象~~。
继承折腾DataView,传说DataView也能直接绑定控件的,yo~~有一丝想法。。

  于是看一下其实现IList接口的源码,发现一堆都在操作DataRowView

public class DataRowView : ICustomTypeDescriptor, IEditableObject, IDataErrorInfo, INotifyPropertyChanged

  没法忽悠了:

你个XX,从DataTable-》DataView-》DataRowView,再转我头就晕了~~。
又是一堆很陌生的接口,于是到这里,我几乎停止了脚步,因为我分析不下去了~~。

  上WC仔细从头想过:

  对于IList<实体>绑定,所有的属性都会被认为是列名,其值为行的值。而对于DataTable,里面又是怎么认识出列名和分析出值的呢?

1:从DataTable中,我们看到一丝列名提取的相关方法,只是返回->DataRow。
2:从DataRow中也看不到提取列名的方法,其关键性的IList接口的相关实现引出了->DataRowView。
3:DataRowView?是神秘的所在?一堆继承的接口也是很陌生。

  回头继续搜索:

  转换思路继续大量搜索:换了很多关键字,搜中文又搜E文。结果尽是一堆自定义控件开发的东东,结果印象中在某一篇的googleE文的“网页快照”中发现一段E文,原文不知是哪了,上次都记得只能打开快照,现在估计能快照都没了,按想象翻译出来的中文大致为:

DataTable能实现其绑定,是因为其实现了ICustomTypeDescriptor,从而获得其属性。

  偶滴神啊~能从千军万马的E文中,扫到几个关键字不容易啊!!!

如果回过头看上面的DataRowView,就会发现,正好,它实现了接口ICustomTypeDescriptor,
只是遥想当年,我并不像现在写文这么冷静,我当初早把Reflector关掉了,哪还记得DataRowView实现了ICustomTypeDescriptor,
再说ICustomTypeDescriptor对我又是那么的陌生,是那么的陌生,...很陌生。。。

  秘密已经出来了:

ICustomTypeDescriptor接口,一个移动控件开发人员经常打交道的接口,对于我们却极为陌生的接口。
是它,就是它,就是它实现如何识别哪些是列名,哪些是列值。

  3:浅入ICustomTypeDescriptor 

当初我通过大量的搜索,试图找到相关的应用示例,因为那时我不知道DataRowView,要是知道,我就不用那么辛苦去搜文章了。
如果你搜索此接口,你会发现一堆的文章都是说移动控件开发,我就是从移动控件开发中很辛苦的挖了点示例实现了。

  不过此文就不走弯路了,直接分析DataRowView,对于 ICustomTypeDescriptor接口,有很多方法: 

public interface 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对此接口的实现:

PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
    if (this.dataView.Table == null)
    {
        return zeroPropertyDescriptorCollection;
    }
    return this.dataView.Table.GetPropertyDescriptorCollection(attributes);
}

  继续深入:

internal PropertyDescriptorCollection GetPropertyDescriptorCollection(Attribute[] 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是什么?继续深入:

internal DataColumnPropertyDescriptor(DataColumn dataColumn) : base(dataColumn.ColumnName, null)
{
    this.column = dataColumn;
}

  两行代码,那个base是啥?是PropertyDescriptor ,实现很简单,把列名传过去就行了,至此,就结束了。不知道有多少会看到这里,估计本文大伙也就是扫下来,除非某天要应用到,不然只是忽悠下眼球了。

  总结下具体实现ICustomTypeDescriptor接口方法:

1:继承实现接口方法。
2:重点实现GetProperties(Attribute[] attributes)方法。
3:需要自定义属性描述类,而这自定义的属性描述类需要继承自抽象基类PropertyDescriptor。
4:GetProperties返回的是自定义属性描述类的集合。

  三、绑定原理分析完,MDataTable模仿出击

  1:MDataTable继承IListSource接口实现

       #region IListSource 成员
        public bool ContainsListCollection
        {
            get
            {
                return true;
            }
        }
        public IList GetList()
        {
            return Rows;
        }
        #endregion

  2:MDataRow继承ICustomTypeDescriptor接口实现

  A:先实现自定义属性描述类

自定义属性描述类MDataProperty

internal class MDataProperty : System.ComponentModel.PropertyDescriptor
    {
        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)

        int index = 0;
        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 原理高级篇(三十一)

  四、总结

微软很强大,MB的Silverlight不支持DataTable的绑定,难道我又要去追随?研究其绑定本质?
不追了,MDataTable增加了ToJson方法和ToList<实体>方法,可直接用json传过去再用反json系列化解析成List<实体>型就可以直接绑定了。

时间: 2024-08-01 11:05:55

一起谈.NET技术,DataTable 深入解析数据源绑定原理之高级篇的相关文章

DataTable 深入解析数据源绑定原理之高级篇

前言 在上篇写了篇 实战系列之天气预报实时采集 ,有个别同志认为没技术含量,也许正如所说. 只是人各有看法,当我写出一篇文章时,我只是希望: 1:如果你还不懂,请看写法,了解想法. 2:如果你已懂,略过写法,请看想法. 其实纵观我一直写来的200多篇文章,基本都可以看出那么点痕迹: 一:没有水文. 二:没有华丽理论型的文章. 三:实战型文章很多. 四:文章尽量面向新手的表述,尽量了. 一.Winform下的DataGridView不支持使用DataReader绑定 1:问题产生 在 CYQ.Da

一起谈.NET技术,2010 .NET面试题整理之基础篇

开篇语:对于已有工作经验的朋友,也许面试题已显得不怎么重要,但是如果你应聘的还仅仅是个普通的程序员,相信在很多的公司都还是会先拿出一套面试题,可能对整个面试影响不大,但做好面试题无疑会赢得第一个好的印象,特别对于那些缺少项目经验的应届毕业生.很多时候,在看这些面试题的时候,是否有感过曾经那些一个个不起眼的小程序题所针对的问题正是自己在项目中所犯的错误?是否会发现,原来还有这么多东西自己都还从未去想过?趁自己这次重新找工作之际,对常见面试题进行进行一次重新整理,与大家共同学习!本贴将会进行不断完善

一起谈.NET技术,ASP.NET的运行原理与运行机制

当一个HTTP请求到服务器并被IIS接收到之后,IIS首先通过客户端请求的页面类型为其加载相应的.dll文件,然后在处理过程中将这条请求发送给能够处理这个请求的模块.在ASP.NET 3.5中,这个模块叫做HttpHandler(HTTP处理程序组件),之所以.aspx文件可以被服务器处理,就是因为在服务器端有默认的HttpHandler专门处理.aspx文件.IIS在将这条请求发送给能够处理这个请求的模块之前,还需要经过一些HttpModule的处理,这些都是系统默认的Modules(用于获取

一起谈.NET技术,学习Linq经验总结

Linq有很多值得学习的地方,这里我们主要介绍学习Linq,包括介绍Linq目标是实现语言与数据的深度结合等方面. 上一个系列讲了C#3.0的新特性,为学习Linq做好了铺垫:接下来的一段时间转入学习Linq,上述新特性也会在介绍的过程中提及到. 学习Linq 在我们的软件中,数据的重要性不可言喻,特别是象ERP,CRM等等这类商业应用软件就是围绕着数据转:然而数据的来源各种各样,如存放在内存中的业务对象.存放在xml文件的数据.SqlServer关系数据库...这些数据源的读取操作各不相同,相

【Alljoyn】Alljoyn学习笔记五 AllJoyn开源技术基础概念解析

AllJoyn开源技术基础概念解析 摘要: 总线(Bus) 实现P2P通信的基础 AllJoyn 的底层协议类似于D-Bus,相当于是跨设备分布式的 D-Bus 总线附件(Bus Attachment) 每一个连接到总线上的Alljoyn应用程序被称为总线附件,可用C++或Java编写 每个总线附件 ... 总线(Bus) 实现P2P通信的基础 AllJoyn 的底层协议类似于D-Bus,相当于是跨设备分布式的 D-Bus总线附件(Bus Attachment) 每一个连接到总线上的Alljoy

113_《DELPHI接口技术开发实例解析》

<DELPHI接口技术开发实例解析> Delphi 教程 系列书籍 (113) <DELPHI接口技术开发实例解析> 网友(邦)整理 EMail: shuaihj@163.com 下载地址: 下载 作者: 张仿彦 出版社:机械工业出版社 ISBN:7111203216 上架时间:2006-12-28 出版日期:2007 年1月 开本:16开 版次:1-1 内容简介 本书包括食堂管理系统.程控电话计费系统.批发零售管理系统.vod点播系统.钢筋拉伸试验管理系统5个案例,这5个不同行业

《创业家》牛文文:少谈点模式多谈点技术

"模式"如同当年的"主义",流行于各种创业大赛.创业励志节目.论坛的"街头"式秀场 文/创业家 牛文文 "美国某某公司你知道吧?就是刚被戴尔.惠普.思科十几亿美元抢购的那家.我们的模式和它的一样,现在还没赢利,可将来起码有十几亿人民币的市值." "我开了小煤矿,但煤运不出去,上商学院之后受到启发,想搞模式创新,具体讲就是想在铁路边上搞个煤炭物流开发区,建一个大的物流和信息流平台,把分散的煤炭集中在我这个园区,这样和铁

数据绑定技术—将DataReader做为数据源绑定到DataGrid控件

SqlDataReader dr; void Page_Load(object sender, System.EventArgs e) { // 数据连接字符串及 SQL 语句 string ConnStr = System.Configuration.ConfigurationSettings.AppSettings["ConnectionSqlServer"]; string query = "SELECT * FROM Categories"; // 创建并打

一起谈.NET技术,asp.net控件开发基础(21)

上篇介绍了在asp.net2.0版本下面如何简单的定义数据绑定控件.虽然DataBoundControl为我们提供了便利,我们以后可以从此类开始编写数据绑定控件.但是在2.0版本未到来之前,你已经为自己订制了一些数据绑定控件,既然2.0版本已经提供了数据源控件,你是否有想法,让你原有的控件也升级到同时支持通过设置DataSource属性和数据源控件来获取数据源,这样以后我们就可以省省工作了.这次我们就来讨论这个话题,让旧版本的数据绑定控件支持数据源控件. 一.准备升级数据绑定控件 即使asp.n