65.9K
CodeProject 正在变化。 阅读更多。
Home

Auto-SQL DataGrid 组件第二部分: 功能齐全的版本

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.36/5 (6投票s)

2005 年 8 月 9 日

5分钟阅读

viewsIcon

38693

downloadIcon

945

ASQLDataGrid 是一个在处理 RDBMS 应用程序时非常实用的组件。它能够根据其属性自动构建和执行 SQL 语句。

引言

在我们系列的第二篇文章中,我们将使我们的 ASQLDataGrid 类可编辑——也就是说,该类将能够自动构建其他 SQL 语句:INSERTUPDATEDELETE。请参阅第一篇文章了解前提。在深入细节之前,让我先介绍一下我们 ASQLDataGrid 类的一些特性。首先,尽管我们的 ASQLDataGrid 是可编辑的,但一次只能编辑一行,即使是新添加的行或现有行。该类的第二个特点是,它将在每行数据上显示按钮(编辑和删除),而不是像其他 DataGrid 示例中那样显示超链接。

CmdButtonColumn 类

CmdButtonColumn 是我在第一篇文章中介绍的 CmdButtonDataGrid 列版本。它有四个 public 属性

  • Cmd:要执行的命令(例如:DG_NEWDG_SAVE)。
  • ButtonText:当行处于视图模式时显示的按钮文本。
  • LabelText:当行处于编辑模式时显示的标签文本。
  • PreStatement:在命令执行之前执行的 JavaScript 代码。它用于在删除行之前添加确认。必要的 JavaScript 在 DataGridItemDataBound 事件中附加到列单元格中的每个按钮上,如下面的代码所示:
    protected override void OnItemDataBound(DataGridItemEventArgs e)
    {
        base.OnItemDataBound(e);
    
        if ((e.Item.ItemType == ListItemType.Item) || 
            (e.Item.ItemType == ListItemType.AlternatingItem) || 
            (e.Item.ItemType == ListItemType.SelectedItem))
        {
            for (int i=0; i<Columns.Count;i++)
            {
              if (Columns[i] is CmdButtonColumn)
              {
                CmdButtonColumn column = (CmdButtonColumn)Columns[i];
                ((Button)e.Item.Cells[i].Controls[0]).Attributes.Add(
                         "OnClick", column.PreStatement + 
                         "__SetCmd('" + column.Cmd + "','" + this.ID + 
                         "$" + e.Item.ItemIndex.ToString() + "');"); 
              }
            }
        }
    }

ASQLDataGridCmdExecutor 类

与第一篇文章中的版本相比,我们的标准命令执行器将处理一些新命令,例如:

  • DG_NEW:将新行添加到 DataGrid 的顶部。
  • DG_CANCEL:取消当前编辑(无论是添加新行还是修改现有行)。
  • DG_SAVE:保存更改。
  • DG_EDIT:将一行设置为编辑模式。
  • DG_DELETE:从 DataGrid 中删除一行。

实现相当直接,您可以在类的 ExecuteCmd 方法中查看详细信息。

ASQLDataGridStatBuilder 类

除了第一篇文章中已有的 BuildSelect 方法外,我们还将有三个新方法来构建 INSERTUPDATEDELETE 语句。让我们先仔细看看 BuildInsert 方法:

public virtual string BuildInsert(ASQLDataGrid dg)
{
    return "insert into " + INS_BuildIntoClause(dg) + 
                         INS_BuildListOfColumns(dg) + 
                         INS_BuildListOfValues(dg);
}

INS_BuildIntoClause 方法从 DataGrid 的 ID 中提取数据库表名,就像 BuildSelect 方法中的 SEL_BuildFromClause 一样。INS_BuildListOfColumns 以与 SEL_BuildListOfColumns 方法相同的方式返回列列表。INS_BuildListOfValues 通过为 DataGrid 中的每列调用 GetColumnValue 方法来构建新行(第 0 行)中每个单元格值的逗号分隔列表。如果列是 BoundColumn,则 GetColumnValue 方法会检查当前单元格是否包含 TextBox 或仅包含文本。返回值将是 TextBox 的当前文本或单元格本身的文本。

if (dg.Columns[ColumnIndex] is BoundColumn)
{
    if (dg.Items[RowIndex].Cells[ColumnIndex].HasControls())
    {
        control = 
          dg.Items[RowIndex].Cells[ColumnIndex].Controls[0];
        if (control is TextBox)
        {
            Value = ((TextBox)control).Text;
        }
    }
    else
    {
        Value = dg.Items[RowIndex].Cells[ColumnIndex].Text;
    }
}

对于 TemplateColumns,当前方法可以获取列包含 ListControlCheckBox 时的单元格值。在任何其他情况下,将返回一个空字符串,这意味着 BuildInsert 方法将在语句中省略该列。事实上,如果数据库列名或其值返回为空字符串,则该列将从语句中省略。当然,您始终可以拥有自己的 Statement Builder,并且如果默认行为不满足您的需求,则可以覆盖语句的任何部分。

这里有一个重要的注意事项是,返回值是“原样”的,没有任何验证或格式化。在您的实际应用程序中,您可能需要在允许 ASQLDataGrid 类自动执行语句之前,添加自己的单元格值验证和格式化。

这是 BuildUpdate 方法的实现方式:

public virtual string BuildUpdate(ASQLDataGrid dg)
{
    string sWhere = UPD_BuildWhereClause(dg);
    if (sWhere.Length == 0)
    {
        //Prevent unintentional update of all the data
        throw new Exception("The update statement " + 
          "requires the correct where clause to work with!");
    }
    return "update " + UPD_BuildTableName(dg) + 
             " set " + UPD_BuildSetClause(dg) + 
             UPD_BuildFromClause(dg) + " where " 
             + sWhere;
}

要构造 UPDATE 语句的 WHERE 子句,我们需要 ASQLDataGrid 的一个新属性:ListOfPKColumns 字符串,其中包含参与数据库表主键的列索引的逗号分隔列表。通常,就像在我们的示例项目中一样,只有一个列——在我们的例子中是 EmployeeID,所以我们将 ListOfPKColumns = "0"UPD_BuildWhereClause 获取 ListOfPKColumns 中的所有列,获取列的数据库名称和当前值的配对,并返回 WHERE 子句,如下所示:

PKColName1 = 'CurrentValue1' and PKColName2 = 'CurrentValue2'

为防止意外更新数据库表中的所有数据,如果 UPD_BuildWhereClause 返回空字符串,BuildUpdate 将抛出异常。UPD_BuildSetClause 方法与 UPD_BuildWhereClause 的工作方式非常相似,只是它处理所有 **不** 在 ListOfPKColumns 中的列。

DELETE 语句的语法是最简单的。这是 BuildDelete 方法的样子:

public virtual string BuildDelete(ASQLDataGrid dg, int RowIndex)
{
    string sWhere = DEL_BuildWhereClause(dg, RowIndex);
    if (sWhere.Length == 0)
    {
        //Prevent unintentional delete of all the data
        throw new Exception("The delete statement " + 
               "requires the correct where clause " + 
               "to work with!");
    }
    return "delete from " + DEL_BuildFromClause(dg) + 
                                   " where " + sWhere;
}

DEL_BuildWhereClauseListOfPKColumns 属性配合使用,就像 UPD_BuildWhereClause 一样。

示例项目

我们将对第一篇文章的示例项目做一些小的修改。首先,我们将 ReportsTo BoundColumn 更改为包含 DropDownListTemplateColumn。此 DropDownList 的内容在 DataGrid 的(实例级别)ItemDataBound 事件处理程序中填充。

private void DG_Employees_ItemDataBound(object sender, 
         System.Web.UI.WebControls.DataGridItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.EditItem)
    {
        //Populating the DropDownList with the employees' names
        ...
    }
}

因为 EmployeeID 是数据库中的标识列——这意味着值将由数据库引擎生成,我们应该指示 Statement Builder 在 INSERT 语句中省略该列。如前所述,如果数据库列名返回为空字符串,则该列将被省略——方法如下:

public override string GetDBColumnName(TypeOfSQLStatement stat, 
                               ASQLDataGrid dg, int ColumnIndex)
{
    ...
        else if (stat == TypeOfSQLStatement.Insert)
        {
            if (ColumnIndex == 0)
            {
                //Skip the EmployeeID because 
                //it is an identity column
                return "";
            }
            else
                return base.GetDBColumnName(stat, 
                                     dg, ColumnIndex);
        }

    ...
}

最后,我们应该为 ListOfPKColumns 属性指定正确的值。由于 Employee 数据库表的主键是 EmployeeID 列,我们将 ListOfPKColumns="0"

结论

我最早的自动 SQL 组件是在 1993 年用一种名为 Gupta SQL Windows 4.0 的编程语言编写的(非常感谢 Németh Miklós),从那时起,我就一直将其包含在我的类库中,包括我最近的 .NET 类库。您可能需要一些时间来掌握这个想法,但我希望您会发现它很有用,就像我和我的团队一样。

编程愉快,富有创造力!

历史

  • 2005 年 8 月 8 日 - 初始版本。
© . All rights reserved.