DataTable 深入解析數據源綁定原理之高級篇

作者: 路過秋天  來源: 博客園  發布時間: 2010-11-02 14:53  閱讀: 2431 次  推薦: 0   原文鏈接   [收藏]  

  前言

  在上篇寫了篇 實戰系列之天氣預報實時采集 ,有個別同志認為沒技術含量,也許正如所說。

只是人各有看法,當我寫出一篇文章時,我只是希望:
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(thistrue);
                defaultView.SetIndex2(
"", DataViewRowState.CurrentRows, nulltrue);
            }
            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<實體>型就可以直接綁定了。
0
0
 
標簽:.Net DataTable
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()