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

ASP.NET 伺服器控件開發基礎 (23):強化資料源控件功能實作

     編輯:關於ASP.NET

系列回顧與本篇目標

前幾篇介紹了基本資料源控件的骨架實作。本篇將深入實作 SqlDataSource 風格的進階功能,包含資料操作事件、自動重新綁定機制、CRUD 權限控制,構建生產級可用的資料源控件核心。

核心概念:資料操作生命週期事件

ASP.NET 內建資料源控件 (SqlDataSource, ObjectDataSource, LinqDataSource) 都提供豐富的前後事件,讓開發者在資料操作前後注入自訂邏輯。標準事件模式如下:

操作前置事件後置事件事件參數類別
SelectSelectingSelectedSqlDataSourceSelectingEventArgs / SqlDataSourceStatusEventArgs
InsertInsertingInsertedSqlDataSourceCommandEventArgs / SqlDataSourceStatusEventArgs
UpdateUpdatingUpdatedSqlDataSourceCommandEventArgs / SqlDataSourceStatusEventArgs
DeleteDeletingDeletedSqlDataSourceCommandEventArgs / SqlDataSourceStatusEventArgs

Select 操作完整實作


protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
    // 1. 建立命令物件 (假設 _command 為 DbCommand)
    DbCommand command = CreateCommand();
    
    // 2. 觸發 Selecting 事件 - 允許取消或修改命令
    SqlDataSourceSelectingEventArgs selectArgs = new SqlDataSourceSelectingEventArgs(command, arguments);
    OnSelecting(selectArgs);
    
    if (selectArgs.Cancel)
        return null; // 使用者取消操作

    // 3. 執行實際查詢
    IEnumerable result = ExecuteQuery(command);

    // 4. 觸發 Selected 事件 - 提供執行狀態資訊
    SqlDataSourceStatusEventArgs statusArgs = new SqlDataSourceStatusEventArgs(command, 
        (result as ICollection)?.Count ?? -1, null);
    OnSelected(statusArgs);

    return result;
}

自動重新綁定機制:DataSourceViewChanged 事件

當資料發生變更 (Insert/Update/Delete) 時,資料源控件必須通知繫結的資料綁定控件 (GridView、FormView 等) 重新查詢資料。這透過 DataSourceView.OnDataSourceViewChanged 方法實現:


protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues)
{
    DbCommand command = CreateDeleteCommand(keys, oldValues);
    
    // 1. 觸發 Deleting 事件
    SqlDataSourceCommandEventArgs deleteArgs = new SqlDataSourceCommandEventArgs(command);
    OnDeleting(deleteArgs);
    if (deleteArgs.Cancel) return 0;

    int affectedRows = 0;
    Exception exception = null;
    
    try
    {
        // 2. 執行刪除
        affectedRows = command.ExecuteNonQuery();
    }
    catch (Exception ex)
    {
        exception = ex;
        throw; // 保持原始堆疊追蹤
    }
    finally
    {
        // 3. 關鍵:通知視圖已變更,觸發自動重新綁定
        OnDataSourceViewChanged(EventArgs.Empty);
        
        // 4. 觸發 Deleted 事件
        SqlDataSourceStatusEventArgs statusArgs = new SqlDataSourceStatusEventArgs(command, affectedRows, exception);
        OnDeleted(statusArgs);
    }
    
    return affectedRows;
}

CRUD 權限控制:Can* 屬性模式

內建資料源控件提供 CanDelete、CanInsert、CanUpdate、CanPage、CanSort 等屬性,讓開發者聲明式控制功能啟用:


// 類別層級定義
[DefaultValue(true)]
public virtual bool CanDelete { get; set; } = true;

[DefaultValue(true)]
public virtual bool CanInsert { get; set; } = true;

[DefaultValue(true)]
public virtual bool CanUpdate { get; set; } = true;

// 在各 Execute* 方法開頭檢查
protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues)
{
    if (!CanDelete)
        throw new NotSupportedException("此資料源控件不支援刪除操作。請設定 CanDelete='true' 啟用。");
    
    // ... 後續邏輯
}

protected override int ExecuteInsert(IDictionary values)
{
    if (!CanInsert)
        throw new NotSupportedException("此資料源控件不支援新增操作。");
    
    // ... 後續邏輯
}

protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
{
    if (!CanUpdate)
        throw new NotSupportedException("此資料源控件不支援更新操作。");
    
    // ... 後續邏輯
}

進階功能:衝突檢測與樂觀鎖定

支援 ConflictDetection 屬性 (OverwriteChanges | CompareAllValues):


public enum ConflictDetection
{
    OverwriteChanges,   // 直接覆蓋 (預設)
    CompareAllValues    // 比較所有舊值,偵測並發衝突
}

[DefaultValue(ConflictDetection.OverwriteChanges)]
public virtual ConflictDetection ConflictDetection { get; set; }

protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
{
    if (ConflictDetection == ConflictDetection.CompareAllValues)
    {
        // 建立 WHERE 條件包含所有舊值
        // 例如:UPDATE Table SET Name=@Name WHERE Id=@Id AND OldName=@OldName AND OldEmail=@OldEmail
        // 若 affectedRows == 0 則拋出 DBConcurrencyException
    }
    // ...
}

參數化查詢與防止 SQL 注入


private DbCommand CreateSelectCommand()
{
    DbCommand cmd = _factory.CreateCommand();
    cmd.CommandText = SelectCommand;
    cmd.CommandType = CommandType.Text; // 或 StoredProcedure
    
    // 自動從 SelectParameters 集合建立參數
    foreach (Parameter param in SelectParameters)
    {
        DbParameter dbParam = _factory.CreateParameter();
        dbParam.ParameterName = param.Name;
        dbParam.Value = param.GetValue(HttpContext.Current, _control);
        dbParam.DbType = param.DbType;
        dbParam.Direction = ParameterDirection.Input;
        cmd.Parameters.Add(dbParam);
    }
    return cmd;
}

視圖狀態持久化


protected override object SaveViewState()
{
    object baseState = base.SaveViewState();
    object parametersState = ((IStateManager)SelectParameters).SaveViewState();
    // ... 儲存其他參數集合
    return new Pair(baseState, parametersState);
}

protected override void LoadViewState(object savedState)
{
    if (savedState == null) return;
    Pair state = (Pair)savedState;
    base.LoadViewState(state.First);
    ((IStateManager)SelectParameters).LoadViewState(state.Second);
    // ... 載入其他參數集合
}

設計時支援:屬性編輯器與智慧標籤


[Designer(typeof(CustomDataSourceDesigner))]
[ToolboxBitmap(typeof(SqlDataSource))]
public class CustomDataSource : DataSourceControl
{
    // ...
}

// 設計器提供智慧標籤:設定連線字串、配置查詢、重新整理結構描述
public class CustomDataSourceDesigner : DataSourceDesigner
{
    public override DesignerActionListCollection ActionLists
    {
        get
        {
            DesignerActionListCollection lists = new DesignerActionListCollection();
            lists.Add(new CustomDataSourceActionList(this));
            return lists;
        }
    }
}

總結

本篇完整實作了企業級資料源控件的核心功能:

  1. 完整事件生命週期:Selecting/Selected、Inserting/Inserted、Updating/Updated、Deleting/Deleted
  2. 自動重新綁定:透過 OnDataSourceViewChanged 實現資料變更後的 UI 同步
  3. 聲明式權限控制:CanDelete/CanInsert/CanUpdate 屬性
  4. 並發衝突處理:支援樂觀鎖定與 ConflictDetection
  5. 參數化安全查詢:防止 SQL 注入的參數集合管理
  6. 視圖狀態持久化:跨 Postback 保持參數與設定

這些功能構成了專業資料源控件的骨幹,掌握這些核心機制,即可開發出媲美內建 SqlDataSource 的客製化資料源控件。

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