更新多个字段的模式






2.56/5 (3投票s)
2005 年 4 月 14 日
4分钟阅读

40331
一篇介绍如何只更新网页表单中已更改的特定值的模式的文章。
引言
您经常会发现自己需要将网页表单中的值更新到数据库中。这通常是一个直接的过程。但是,如果您只想更新网页表单中已更改的那些值,会发生什么?并且,考虑到将来需要更新的值集可能会增加(或不太可能减少),您是否想要一个易于使用的模式?在此示例中,我将介绍更新个人信息的案例。
背景
可能您有一个用户可以更新其个人信息的表单。无论您使用的是 Windows Forms 还是 Web Forms(在此示例中我将使用 Web Forms),您都会使用一系列 Web 控件来捕获数据(下拉列表、文本框、单选按钮等)。而且,您可能不想更新存储此信息的表中的每个值,因为存在某些触发器会在值更新时执行某些操作。或者,您可能有大量的访问量,只是不想让事务日志填满。或者,您的用户可能智力有限,即使他们没有更新任何内容也经常点击“提交”按钮,而您不想不加限制地发出调用。以下解决方案将尝试提供一种处理这种情况的模式。
使用代码
该方法论基本上是维护一个参数名称/参数值的哈希表,只考虑在网页表单中已更改的那些值。通过填充我们声明的 System.Web.UI.Page
类实例变量,我们可以将此值传递给负责发出数据库更新调用的某个方法。此更新方法将提取这些参数名称和值,并发出对负责更新的存储过程的调用。
执行此操作的代码如下:
- 创建一个
System.System.Collections.Hashtable
的private
实例变量。此变量将负责将存储过程参数名称与用户新创建的(并且保证不同的 - 稍后会详述)值关联起来。private System.Collections.Hashtable _changeList;
- 创建一个
private
实例方法,该方法使用上面提到的键/值参数填充表。private void Add2Collection(string name, string newValue) { if(null == _changeList) { //must issue the constructor here. //We want _changeList to be null if nothing changed. _changeList = new Hashtable(); } _changeList.Add(name, newValue); }
- 连接每个 Web 控件的
ValueChanged
事件。例如:System.Web.UI.WebControls.TextBox = TextChanged System.Web.UI.WebControls.DropDownList = SelectedIndexChanged System.Web.UI.WebControls.CheckBox = CheckedChanged
现在,aspx 标记应该看起来像这样:
Name <asp:TextBox id="txtName" runat="server" OnTextChanged="txtName_Changed"/> Age <asp:DropDownList id="cmbAge" runat="server" OnSelectedIndexChanged="cmbAge_Changed"> <asp:ListItem Value="0">13-20</asp:ListItem> <asp:ListItem Value="1">21-30</asp:ListItem> <asp:ListItem Value="2">31-40</asp:ListItem> <asp:ListItem Value="3">41-50</asp:ListItem> <asp:ListItem Value="4">>
请注意,我没有为
rbFemale
单选按钮设置事件。这是因为它们都是同一组的成员,并且我断言存储此字段的表定义如下:CREATE TABLE UserData ( ... UserName varchar(50), Age int, IsMale bit, ... )
因此,如果用户是男性并单击
rbFemale
,这将触发对rbMale_Changed()
的调用。如果用户是女性并单击rbMale
,同样会发出对rbMale_Changed()
的调用。希望没有人会经历太多的性别转变。 - 在每个事件中,发出对
Add2Collection()
的调用。public void txtName_Changed(object sender, EventArgs e) { Add2Collection("userName", txtName.Text); } public void cmbAge_Changed(object sender, EventArgs e) { Add2Collection("userAge", cmbAge.SelectedValue); } public void rbMale_Changed(object sender, EventArgs e) { Add2Collection("isMale", rbMale.Checked.ToString()); }
这就是我们如何保证当没有任何更改时
_changeList
将为null
。只有对Add2Collection
的调用才会初始化此变量。如果没有更改,那么此变量将保持null
,并向我们发出标志,表明我们不需要调用数据库。 - 最后,还有一个按钮可以发出更新调用:
<asp:Button id="btnUpdate" runat="server" Text="Update" OnClick="btnUpdate_Click"/>
以及其背后的代码:
public void btnUpdate_Click(object sender, System.EventArgs e) { Update(_changeList); }
更新方法非常简单。它接受
_changeList
,或者在本例中是实现IDictionary
的类。这些条目现在保证包含参数名称及其新值。我们使用枚举器提取这些对,以收集参数名称和参数值,将它们加载到SqlCommand
中并发出调用。当然,如果IDictionary
对象为null
,我们只需返回。private static void Update(IDictionary parameterCollection) { //user hit update but didn't update anything - just return if(null == parameterCollection) { return; } const string sproc = "SomeUpdateSproc"; using(SqlConnection connection = new SqlConnection(SomeConnectionString)) using(SqlCommand command = new SqlCommand(sproc, connection)) { command.CommandType = CommandType.StoredProcedure; //get every name/value pair foreach(DictionaryEntry entry in parameterCollection) { //don't forget to add the '@' SqlParameter parameter = command.Parameters.Add("@" + entry.Key, entry.Value); parameter.Value = entry.Value; //our new value! } command.Connection.Open(); command.ExecuteNonQuery(); command.Connection.Close(); } }
我将此方法设为
static
并将实例变量作为参数接受,因为这将封装在一些数据访问层代码中。只需将其移出代码隐藏页,放入数据访问层即可。最后,存储过程将如下所示:
CREATE PROCEDURE [dbo].[SomeUpdateSproc] @userName varchar(50) = null, @userAge int = null, @isMale bit = null AS if @userName <> null begin --do some update end if @userAge <> null begin --do some update end if @isMale <> null begin --do some update end GO
就这样。随着新的字段被添加到需要以相同方式更新的表中,只需添加相应的 Web 控件,设置其 Changed
事件处理程序,发出 Add2Collection()
调用,并向存储过程添加适当的逻辑。
历史
目前还没有。