首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET 伺服器控件開發基礎 (21):舊版資料綁定控件升級支援 DataSourceID

     編輯:關於ASP.NET

遷移背景與挑戰

ASP.NET 1.x 時代開發的自訂資料綁定控件 (繼承 BaseDataList 或 Control) 僅支援 DataSource 屬性 + DataBind() 模式。ASP.NET 2.0 引入資料源控件 (SqlDataSource 等) 後,新架構採用 DataSourceID 宣告式繫結,自動處理資料查詢與重新綁定。如何在不重寫控件邏輯的前提下,讓舊版控件支援新架構?本篇提供完整遷移方案。

新舊繫結模式差異比較

特性舊模式 (1.x)新模式 (2.0+)
資料來源指定DataSource 屬性指派物件DataSourceID 指定資料源控件 ID
繫結觸發手動呼叫 DataBind()自動於 PreRender 階段觸發
資料查詢開發者自行撰寫資料源控件封裝
分頁/排序自行實作資料源控件自動支援
狀態管理手動處理框架自動管理

核心介面:IDataBoundControl 與 IDataBoundListControl


public interface IDataBoundControl
{
    string DataSourceID { get; set; }
    IDataSource DataSourceObject { get; }
    DataSourceView DataSourceView { get; }
    void DataBind();
}

public interface IDataBoundListControl : IDataBoundControl
{
    string DataMember { get; set; }
    DataSourceSelectArguments SelectArguments { get; }
}

關鍵成員實作清單

為支援 DataSourceID,控件必須實作以下核心成員 (參考 BaseDataBoundControl):

1. 基礎屬性


// 是否已初始化
protected bool Initialized { get; private set; }

// 資料成員 (DataSet 多表時指定表名)
public string DataMember
{
    get => ViewState["DataMember"] as string ?? String.Empty;
    set 
    { 
        ViewState["DataMember"] = value; 
        OnDataPropertyChanged(); 
    }
}

// 直接指派資料源 (舊模式相容)
public IEnumerable DataSource
{
    get => _dataSource;
    set 
    {
        if (value is IEnumerable || value is IListSource || value == null)
            _dataSource = value;
        else
            throw new ArgumentException("資料源必須實作 IEnumerable 或 IListSource");
        OnDataPropertyChanged();
    }
}
private IEnumerable _dataSource;

// 資料源控件 ID (新模式核心)
[DefaultValue(""), IDReferenceProperty(typeof(DataSourceControl))]
public virtual string DataSourceID
{
    get => ViewState["DataSourceID"] as string ?? String.Empty;
    set 
    { 
        ViewState["DataSourceID"] = value; 
        OnDataPropertyChanged(); 
    }
}

// 是否使用 DataSourceID 模式
protected bool IsBoundUsingDataSourceID => DataSourceID.Length > 0;

// 是否需要繫結資料
protected bool RequiresDataBinding
{
    get => _requiresDataBinding;
    set => _requiresDataBinding = value;
}
private bool _requiresDataBinding;

// 查詢參數
protected DataSourceSelectArguments SelectArguments
{
    get
    {
        if (_selectArguments == null)
            _selectArguments = CreateDataSourceSelectArguments();
        return _selectArguments;
    }
}
private DataSourceSelectArguments _selectArguments;

2. 核心方法:資料源解析與繫結流程


// 取得資料源控件實例
protected IDataSource GetDataSource()
{
    if (IsBoundUsingDataSourceID)
    {
        Control control = FindControl(DataSourceID);
        if (control == null)
            throw new HttpException($"找不到 ID 為 '{DataSourceID}' 的資料源控件");
        return control as IDataSource ?? 
            throw new HttpException($"控件 '{DataSourceID}' 非資料源控件");
    }
    return null;
}

// 取得資料源視圖
protected DataSourceView GetDataSourceView()
{
    IDataSource dataSource = GetDataSource();
    return dataSource?.GetView(String.Empty);
}

// 核心繫結邏輯
protected override void PerformDataBinding(IEnumerable data)
{
    // 1. 如果使用 DataSourceID,從視圖取得資料
    if (IsBoundUsingDataSourceID)
    {
        DataSourceView view = GetDataSourceView();
        if (view != null)
        {
            // 這裡會自動觸發 Selecting/Selected 事件
            data = view.ExecuteSelect(SelectArguments);
        }
    }
    
    // 2. 呼叫子類別實作的實際繫結邏輯
    if (data != null)
    {
        PerformSelect(data);
    }
    
    // 3. 重設繫結需求旗標
    RequiresDataBinding = false;
    Initialized = true;
}

// 子類別必須實作:實際將資料繫結到 UI
protected abstract void PerformSelect(IEnumerable data);

// 資料屬性變更通知
protected virtual void OnDataPropertyChanged()
{
    if (Initialized)
        RequiresDataBinding = true;
}

3. 生命週期整合:確保正確時機繫結


protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    
    // 關鍵:PreRender 階段自動執行繫結
    if (RequiresDataBinding)
    {
        EnsureDataBound();
    }
}

public override void DataBind()
{
    // 支援手動呼叫 DataBind() (舊模式相容)
    RequiresDataBinding = true;
    EnsureDataBound();
    base.DataBind(); // 呼叫 Control.DataBind() 觸發子控件繫結
}

protected void EnsureDataBound()
{
    if (RequiresDataBinding && !IsBoundUsingDataSourceID && DataSource == null)
        return; // 無資料源則跳過
    
    PerformDataBinding(null); // null 表示從 DataSourceView 取得
    MarkAsDataBound();
}

protected virtual void MarkAsDataBound()
{
    // 子類別可覆寫標記已繫結狀態
}

遷移實戰:從 BaseDataList 升級

原始舊版控件結構


public class LegacyRepeater : BaseDataList
{
    // 1.x 風格:僅支援 DataSource + DataBind()
    protected override void PerformSelect()
    {
        IEnumerable data = ResolveDataSource(); // 內部處理 DataSource
        if (data != null)
        {
            // 建立控件階層、繫結模板...
        }
    }
}

升級後支援 DataSourceID


public class ModernRepeater : BaseDataList // BaseDataList 2.0 已支援 DataSourceID
{
    // 1. 繼承 BaseDataList 即自動獲得 DataSourceID 支援
    // 2. 只需覆寫 PerformSelect(IEnumerable) 新簽名
    protected override void PerformSelect(IEnumerable data)
    {
        // 統一處理邏輯:data 可能來自 DataSource 或 DataSourceView
        if (data == null && IsBoundUsingDataSourceID)
        {
            data = GetDataSourceView()?.ExecuteSelect(SelectArguments);
        }
        
        if (data != null)
        {
            // 原有模板建立、資料繫結邏輯保持不變
            CreateControlHierarchy(data);
            ChildControlsCreated = true;
        }
    }
    
    // 3. 確保 OnDataPropertyChanged 觸發 RequiresDataBinding
    protected override void OnDataPropertyChanged()
    {
        base.OnDataPropertyChanged();
        RequiresDataBinding = true;
    }
}

關鍵注意事項

  1. 互斥原則:DataSource 與 DataSourceID 不可同時使用,否則拋出異常
  2. DataBind() 行為差異:設定 DataSourceID 後不需呼叫 DataBind(),框架自動在 PreRender 執行
  3. 視圖狀態同步:參數集合 (SelectParameters 等) 需正確實作 IStateManager
  4. 設計時支援:加入 [IDReferenceProperty(typeof(DataSourceControl))] 讓 VS 屬性視窗提供下拉選單

總結

透過實作 IDataBoundControl 核心成員、整合生命週期 (OnPreRender 自動繫結)、統一 PerformSelect 入口,舊版資料綁定控件可平滑升級支援 DataSourceID 宣告式繫結。此遷移策略保留原有模板與繫結邏輯,僅擴展資料來源解析能力,是低風險、高回報的現代化改造路徑。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved