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

在 ASP.NET 中使用 JSON 更新 GridView 的多行

starIconstarIconstarIconstarIconstarIcon

5.00/5 (17投票s)

2014 年 6 月 20 日

CPOL

7分钟阅读

viewsIcon

83623

downloadIcon

2409

本文将讨论如何在 ASP.NET 中使用 JSON,通过单击一个按钮来更新 GridView 中的多条记录。

引言

如果您是使用 ASP.NET 的 Web 开发人员,那么我假设您一定多次使用过 GridView 控件。毫无疑问,GridView 控件是 ASP.NET 库迄今为止提供的最强大的控件之一。

我们一定多次使用 GridView 控件来允许用户执行 CRUDCreate Read Update Delete)操作。在典型场景中,用户点击特定行的编辑按钮,修改数据,然后点击更新以将更改保存到数据库中。

让我们考虑用户希望一次修改多条记录。修改完成后,用户将点击更新按钮,所有修改过的数据都将更新到数据库中。

用户点击相应的 CheckBox 来编辑行并输入数据

数据输入完成后,用户点击更新按钮将修改后的数据发送到数据库

背景

这可以通过循环遍历 GridView 中的所有行来实现。如果该行被选中进行编辑,则执行 UPDATE 语句或调用存储过程来更新数据库中的数据。

但这种方法,如果我们要更新 20 条记录,那么我们将命中数据库服务器 20 次。这种方法可能不是更新大量记录的非常有效的方式。

如果我们可以一次性将所有(待修改的)数据传递到数据库,并且所有行都将更新,那该多好!听起来不错!但最大的问题是如何实现?

我们知道 JSON 是一种非常轻量级的数据交换技术,可以通过网络传输数据。那么,将数据以 JSON 格式发送到 SQL Server 如何呢!我们可以创建一个包含所有所需数据的 JSON 字符串,并将其传递给存储过程。然后存储过程将解析数据并创建动态的 UPDATE 语句并执行。

挑战

我们将面临的主要挑战是在存储过程中解析 JSON 字符串,因为目前 SQL Server 中还没有可用的系统函数。然后我找到了一个救星——由 **Phill Factor** 撰写的关于“在 SQL Server 中使用 JSON 字符串”的文章。他用 T-SQL 编写了一个出色的 JSON 解析函数,名为 parseJSON(),它接受一个 JSON string 并将数据返回到临时表格式。我将利用 parseJSON() 函数来完成我的演示。

请务必下载并执行 _parseJSON.sql_ 脚本以创建所需的解析函数。您也可以访问以下链接

数据库更改

让我们快速设置数据库环境,创建一个表 - tblStudent 并插入一些虚拟数据

CREATE TABLE [dbo].[tblStudent](
    [StudentID] [int] NOT NULL,
    [Name] [varchar](50) NULL,
    [C] [int] NULL,
    [CPP] [int] NULL,
    [CS] [int] NULL
) ON [PRIMARY]

INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(1,'Amitabh Bachchan')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(2,'Saif Ali Khan')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(3,'Ranbir Kapoor')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(4,'Shahid Kapoor ')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(5,'Ajay Devgan')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(6,'Hritik Roshan')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(7,'Akshay Kumar')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(8,'Shahrukh Khan')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(9,'Amir Khan')
INSERT INTO [tblStudent] ([StudentID],[Name]) VALUES(10,'Salman Khan')

Using the Code

首先创建一个 ASP.NET 空 Web 应用程序,然后添加一个新的 Web 表单。然后添加以下 GridView 控件以显示 [tblStudent] 表中的详细信息。

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" Width="480px">
    <Columns>
        <asp:TemplateField ItemStyle-Width="20px">
           <ItemTemplate>
              <asp:CheckBox runat="server" AutoPostBack="true" OnCheckedChanged="OnCheckedChanged" />
           </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="StudentID" HeaderText="ID" SortExpression="StudentID"
            ItemStyle-Width="25px" />
        <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
        <asp:TemplateField HeaderText="C" ItemStyle-HorizontalAlign="Center">
            <ItemTemplate>
                <asp:Label runat="server" Text='<%# Eval("C") %>'></asp:Label>
                <asp:TextBox ID="txtC" runat="server" Text='<%# Eval("C") %>'
                    Width="30px" Visible="false" MaxLength="3"></asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="C++" ItemStyle-HorizontalAlign="Center">
            <ItemTemplate>
                <asp:Label runat="server" Text='<%# Eval("CPP") %>'></asp:Label>
                <asp:TextBox ID="txtCPP" runat="server" Text='<%# Eval("CPP") %>'
                    Width="30px" Visible="false" MaxLength="3"></asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="C#" ItemStyle-HorizontalAlign="Center">
            <ItemTemplate>
                <asp:Label runat="server" Text='<%# Eval("CS") %>'></asp:Label>
                <asp:TextBox ID="txtCS" runat="server" Text='<%# Eval("CS") %>'
                    Width="30px" Visible="false" MaxLength="3"></asp:TextBox>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

代码解释

<asp:CheckBox runat="server" AutoPostBack="true" OnCheckedChanged="OnCheckedChanged" />

这里我们使用了一个 CheckBox 控件,其 AutoPostback="true",这样用户每次选中/取消选中任何复选框时都会回发。我们将在代码后端处理 CheckedChanged 事件。

<asp:BoundField DataField="StudentID" HeaderText="ID" SortExpression="StudentID"
            ItemStyle-Width="25px" />
<asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />

上述两个 BoundField 已添加,用于显示 ID 和 Name 列。

<asp:TemplateField HeaderText="C" ItemStyle-HorizontalAlign="Center" ItemStyle-Width="50px">
    <ItemTemplate>
        <asp:Label runat="server" Text='<%# Eval("C") %>'></asp:Label>
        <asp:TextBox ID="txtC" runat="server" Text='<%# Eval("C") %>' 
            Width="30px" Visible="false" MaxLength="3"></asp:TextBox>
    </ItemTemplate>
</asp:TemplateField>

上述 TemplateField 将用于显示科目——C 的分数列。在该列中,我们有两个控件——一个 Label 和一个 TextBox。最初 Label 控件将可见,TextBox 将保持隐藏。一旦用户选中相应的 CheckBox 控件,Label 控件将变为隐藏,TextBox 将变为可见,以便用户可以编辑分数。MaxLength="3" 也已设置,以便用户不能输入超过 3 位数字。

同样,我们还有其他两列用于科目:C++C#

让我们快速编写 LoadData() 以使用 [tblStudent] 表中的数据填充 GridView

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        LoadData();
    }
}

private void LoadData()
{
    string consString = ConfigurationManager.ConnectionStrings["TEST"].ConnectionString;
    using (SqlConnection conn = new SqlConnection(consString))
    {
        SqlCommand cmd = new SqlCommand("SELECT * FROM [tblStudent]", conn);
        cmd.CommandType = CommandType.Text;
        conn.Open();
        SqlDataReader drStudents = cmd.ExecuteReader();
        GridView1.DataSource = drStudents;
        GridView1.DataBind();
    }
}

此时,我们的示例应该从数据库中获取数据并显示在 GridView 上。

现在,让我们在网页上添加另外两个控件——一个 Button 和一个 Label 控件。

  • btnUpdate - 用户将点击此按钮以更新数据库中的更改。
  • lblMessage - 用于向用户显示友好的消息。
<asp:Button ID="btnUpdate" runat="server" Text="Update" OnClick="btnUpdate_Click"
    Visible="false" />
<asp:Label ID="lblMessage" runat="server" ForeColor="Green"></asp:Label>

注意:最初,Button 控件被设置为 Visible="false"。一旦用户选中任何用于编辑的 CheckBoxUpdate 按钮将变为可见。

让我们继续添加 OnCheckedChanged 事件,该事件将在用户每次选中/取消选中任何行时触发。

protected void OnCheckedChanged(object sender, EventArgs e)
{
    bool isUpdateVisible = false;
    Label1.Text = string.Empty;

    //Loop through all rows in GridView
    foreach (GridViewRow row in GridView1.Rows)
    {
        if (row.RowType == DataControlRowType.DataRow)
        {
            bool isChecked = row.Cells[0].Controls.OfType<CheckBox>().FirstOrDefault().Checked;
            if (isChecked)
                row.RowState = DataControlRowState.Edit;

            for (int i = 3; i < row.Cells.Count; i++)
            {
                row.Cells[i].Controls.OfType<Label>().FirstOrDefault().Visible = !isChecked;
                if (row.Cells[i].Controls.OfType<TextBox>().ToList().Count > 0)
                {
                    row.Cells[i].Controls.OfType<TextBox>().FirstOrDefault().Visible = isChecked;
                }
                        
                if (isChecked && !isUpdateVisible)
                {
                    isUpdateVisible = true;
                }
            }
        }
    }
    btnUpdate.Visible = isUpdateVisible;
}

代码解释

bool isChecked = row.Cells[0].Controls.OfType<CheckBox>().FirstOrDefault().Checked;
if (isChecked)
    row.RowState = DataControlRowState.Edit;

这行代码将获取第一列上的 CheckBox 控件是否被选中。如果被选中,那么让我们将 RowState 设置为 Edit。这将确保该行以编辑模式显示。

for (int i = 3; i < row.Cells.Count; i++)

这里我们从第 3 列开始循环遍历其他列,因为我们必须跳过 ID 和 Name 这两列。

if (row.Cells[i].Controls.OfType<TextBox>().ToList().Count > 0)
{
    row.Cells[i].Controls.OfType<TextBox>().FirstOrDefault().Visible = isChecked;
}

在上面的代码中,我们正在检查是否找到了 TextBox 控件。如果是,则根据 CheckBox 选中值设置可见性。如果 CheckBox 被选中,则使 TextBox 可见,否则隐藏 TextBox 控件。

btnUpdate.Visible = isUpdateVisible;

最后,我们确保 btnUpdate 控件只有在至少一个 checkBox 被选中时才可见。

最后一步

我认为现在是时候关注如何将数据发送到 SQL Server 并更新数据库中的数据了。根据我们的要求,我们将以 JSON string 格式将数据发送到一个存储过程,该存储过程将解析输入的 JSON string 并动态创建 UPDATE 语句。

这是一个包含两条记录详细信息的 JSON string 示例

[
 {"ID":"1","C":"60","CPP":"60","CS":"60"},
 {"ID":"2","C":"55","CPP":"60","CS":"60"}
]

所以,我们必须从我们的代码后台生成这个 string 并将其传递给存储过程。

在我们编写生成 string 的代码之前,我想先完成存储过程。

CREATE PROC [dbo].[spUpdateMarks]
    @inputJSON VARCHAR(MAX)  -- '[{"ID":"1","C":"60","CPP":"60","CS":"60"}]'
AS
BEGIN
    DECLARE @UpdateSQL AS VARCHAR(MAX)
    DECLARE @Root_ID AS INT
    DECLARE @parent_ID AS INT
    DECLARE @StudentID AS INT
    DECLARE @C AS INT
    DECLARE @CPP AS INT
    DECLARE @CS AS INT

    -- Temp table to hold the parsed data
    DECLARE @TempTableVariable TABLE(
        element_id INT,
        sequenceNo INT,
        parent_ID INT,
        [Object_ID] INT,
        [NAME] NVARCHAR(2000),
        StringValue NVARCHAR(MAX),
        ValueType NVARCHAR(10)
    )
    -- Parse JSON string into a temp table
    INSERT INTO @TempTableVariable
    SELECT * FROM parseJSON(@inputJSON)
    
    -- Get the Root node ID
    SELECT @Root_ID = MAX([Object_ID]) FROM @TempTableVariable
    
    -- If there is only one record to update
    IF @Root_ID = 1
    BEGIN
        SELECT @StudentID = StringValue FROM @TempTableVariable
        WHERE NAME = 'ID' AND parent_ID = @Root_ID
        
        SELECT @C = StringValue FROM @TempTableVariable
        WHERE NAME = 'C' AND parent_ID = @Root_ID

        SELECT @CPP = StringValue FROM @TempTableVariable
        WHERE NAME = 'CPP' AND parent_ID = @Root_ID

        SELECT @CS = StringValue FROM @TempTableVariable
        WHERE NAME = 'CS' AND parent_ID = @Root_ID
        
        SET @UpdateSQL = ' UPDATE [tblStudent] ' +
                         ' SET C = ' + CAST(@C AS VARCHAR) + ',' +
                         ' CPP = ' + CAST(@CPP AS VARCHAR) + ',' +
                         ' CS = ' + CAST(@CS AS VARCHAR) +
                         ' WHERE StudentID = ' + CAST(@StudentID AS VARCHAR)
        EXECUTE(@UpdateSQL)
        --PRINT @UpdateSQL
    END
    ELSE
    BEGIN
        DECLARE curMarks CURSOR -- Declare cursor
        LOCAL SCROLL STATIC
        FOR
        SELECT [Object_ID] FROM @TempTableVariable
        WHERE [parent_ID] = @Root_ID
        
        OPEN curMarks -- open the cursor
        
        FETCH NEXT FROM curMarks
        INTO @parent_ID
        
        WHILE @@FETCH_STATUS = 0
        BEGIN
            SELECT @StudentID = StringValue FROM @TempTableVariable
            WHERE NAME = 'ID' AND parent_ID = @parent_ID
            
            SELECT @C = StringValue FROM @TempTableVariable
            WHERE NAME = 'C' AND parent_ID = @parent_ID

            SELECT @CPP = StringValue FROM @TempTableVariable
            WHERE NAME = 'CPP' AND parent_ID = @parent_ID

            SELECT @CS = StringValue FROM @TempTableVariable
            WHERE NAME = 'CS' AND parent_ID = @parent_ID
            
            -- Create the UPDATE query dynamically and then execute
            SET @UpdateSQL = ' UPDATE [tblStudent] ' +
                             ' SET C = ' + CAST(@C AS VARCHAR) + ',' +
                             ' CPP = ' + CAST(@CPP AS VARCHAR) + ',' +
                             ' CS = ' + CAST(@CS AS VARCHAR) +
                             ' WHERE StudentID = ' + CAST(@StudentID AS VARCHAR)
            EXECUTE(@UpdateSQL)
            --PRINT @UpdateSQL
            
            FETCH NEXT FROM curMarks INTO @parent_ID
        END
    
        CLOSE curMarks -- close the cursor
        DEALLOCATE curMarks -- Deallocate the cursor
    END
END

请参考内联注释以理解逻辑。如果您需要更多说明,请告诉我,我将更新文章。

让我们完成我们的更新按钮点击事件,以创建如上所示的 JSON string

protected void btnUpdate_Click(object sender, EventArgs e)
{
    try
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("[");
        foreach (GridViewRow row in GridView1.Rows)
        {
            bool isChecked = row.Cells[0].Controls.OfType<CheckBox>().FirstOrDefault().Checked;
            if (isChecked)
            {
                TextBox txtC = (TextBox)row.FindControl("txtC");
                TextBox txtCPP = (TextBox)row.FindControl("txtCPP");
                TextBox txtCS = (TextBox)row.FindControl("txtCS");

                sb.Append("{");
                sb.AppendFormat("\"ID\":\"{0}\",\"C\":\"{1}\",\"CPP\":\"{2}\",\"CS\":\"{3}\"", 
                                row.Cells[1].Text, txtC.Text, txtCPP.Text, txtCS.Text);
                sb.Append("},");
            }
        }

        if (sb.ToString().Length > 1)
        {
            sb.Append("]");

            string inputData = sb.ToString();

            string consString = ConfigurationManager.ConnectionStrings["TEST"].ConnectionString;
            using (SqlConnection conn = new SqlConnection(consString))
            {
                SqlCommand cmd = new SqlCommand("spUpdateMarks", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@inputJSON", inputData);
                conn.Open();
                cmd.ExecuteNonQuery();
            }
            btnUpdate.Visible = false;
            lblMessage.Text = "Data updated successfully!";
            LoadData();
        }
        else
        {
            lblMessage.Text = "No value selected for update!";
        }
    }
    catch (SqlException ex)
    {
        lblMessage.Text = "error" + ex.ToString();
    }
}

代码解释

StringBuilder sb = new StringBuilder();
sb.Append("[");
foreach (GridViewRow row in GridView1.Rows)
{
    bool isChecked = row.Cells[0].Controls.OfType<CheckBox>().FirstOrDefault().Checked;
    if (isChecked)
    {
        TextBox txtC = (TextBox)row.FindControl("txtC");
        TextBox txtCPP = (TextBox)row.FindControl("txtCPP");
        TextBox txtCS = (TextBox)row.FindControl("txtCS");

        sb.Append("{");
        sb.AppendFormat("\"ID\":\"{0}\",\"C\":\"{1}\",\"CPP\":\"{2}\",\"CS\":\"{3}\"", 
                        row.Cells[1].Text, txtC.Text, txtCPP.Text, txtCS.Text);
        sb.Append("},");
    }
}

以上代码行正在创建一个 StringBuilder 对象,通过循环遍历 GridView 中的所有行并连接所需值来创建 JSON 字符串。

其余代码非常简单直接,执行基本的 ADO.NET 操作,连接到数据库并执行存储过程。

代码优化

大家喜欢我创建 JSON 字符串的方式吗?你们不觉得有点笨拙吗?

让我们优化文章中 JSON 字符串的生成部分,并尝试使其结构更清晰。System.Web.Script.Serialization 命名空间中提供了一个 JavaScriptSerializer 类,我们将使用它来将修改后的数据序列化为 JSON 字符串。

让我们添加一个新类 - _Student.cs_ 如下

public class Student
{
    public string ID { get; set; }
    public string C { get; set; }
    public string CPP { get; set; }
    public string CS { get; set; }
}

现在我们将修改 Update 按钮的点击事件,以使用 JavaScriptSerializer 类的 Serialize() 方法来生成 JSON 字符串(请参见 **粗体** 添加的代码)

protected void btnUpdate_Click(object sender, EventArgs e)
{
    try
    {
        //StringBuilder sb = new StringBuilder();
        //sb.Append("[");
        List<Student> students = new List<Student>();

        foreach (GridViewRow row in GridView1.Rows)
        {
            bool isChecked = row.Cells[0].Controls.OfType<CheckBox>().FirstOrDefault().Checked;
            if (isChecked)
            {
                TextBox txtC = (TextBox)row.FindControl("txtC");
                TextBox txtCPP = (TextBox)row.FindControl("txtCPP");
                TextBox txtCS = (TextBox)row.FindControl("txtCS");

                students.Add(new Student()
                {
                    ID = row.Cells[1].Text,
                    C = txtC.Text,
                    CPP = txtCPP.Text,
                    CS = txtCS.Text
                });

                //sb.Append("{");
                //sb.AppendFormat("\"ID\":\"{0}\",\"C\":\"{1}\",\"CPP\":\"{2}\",\"CS\":\"{3}\"",
                //row.Cells[1].Text, txtC.Text, txtCPP.Text, txtCS.Text);
                //sb.Append("},");
            }
        }

        //if (sb.ToString().Length > 1)
        if (students.Count > 0)
        {
            //sb.Append("]");
            //string inputData = sb.ToString();
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            string inputData = serializer.Serialize(students);

            string consString = ConfigurationManager.ConnectionStrings["TEST"].ConnectionString;
            using (SqlConnection conn = new SqlConnection(consString))
            {
                SqlCommand cmd = new SqlCommand("spUpdateMarks", conn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@inputJSON", inputData);
                conn.Open();
                cmd.ExecuteNonQuery();
            }
            btnUpdate.Visible = false;
            lblMessage.Text = "Data updated successfully!";
            LoadData();
        }
        else
        {
            lblMessage.Text = "No value selected for update!";
        }
    }
    catch (SqlException ex)
    {
        lblMessage.Text = "error" + ex.ToString();
    }
}

代码解释

List<Student> students = new List<Student>();

声明了一个 Student 类的 List<> 对象,用于保存所有修改过的记录详细信息。

students.Add(new Student()
{
    ID = row.Cells[1].Text,
    C = txtC.Text,
    CPP = txtCPP.Text,
    CS = txtCS.Text
});

将新的 Student 对象添加到 List<Student> 集合对象。

JavaScriptSerializer serializer = new JavaScriptSerializer();
string inputData = serializer.Serialize(students);

非常简单的代码,它创建了一个 JavaScriptSerializer 类的对象,并通过传递学生列表对象调用 Serialize() 方法。Serialize() 函数将返回一个 JSON 字符串,与我们之前手动创建的相同。:)

现在代码看起来好多了!不是吗?

所有必要的更改都已完成。您可以执行示例,查看单击一次如何以 JSON 格式将整个数据传递给存储过程。然后存储过程通过调用 parseJSON() 解析 JSON,并在动态创建 UPDATE 语句后执行。

请分享您的评论或反馈。如果您喜欢这篇文章,请给个评分。 :)

编码愉快 :)

历史

  • 2014 年 6 月 20 日:初始版本
  • 2014 年 6 月 26 日:增加了代码优化部分
© . All rights reserved.