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





5.00/5 (17投票s)
本文将讨论如何在 ASP.NET 中使用 JSON,通过单击一个按钮来更新 GridView 中的多条记录。
- 下载 UpdateMultipleRowsUsingJSON_v2.zip - 13.5 KB
- 下载 parseJSON.zip - 3.2 KB
- 下载 UpdateMultipleRowsUsingJSON.zip - 12.8 KB
引言
如果您是使用 ASP.NET 的 Web 开发人员,那么我假设您一定多次使用过 GridView
控件。毫无疑问,GridView
控件是 ASP.NET 库迄今为止提供的最强大的控件之一。
我们一定多次使用 GridView
控件来允许用户执行 CRUD(Create 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"
。一旦用户选中任何用于编辑的 CheckBox
,Update
按钮将变为可见。
让我们继续添加 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 日:增加了代码优化部分