在不进行数据绑定的情况下导航和控制记录






3.89/5 (6投票s)
2005 年 2 月 3 日
8分钟阅读

57999

879
另一种在不依赖数据绑定的情况下导航和控制记录的方法。
引言
当我阅读 **Stephen Walther** 撰写的 **《ASP.NET Unleashed》** 一书中的 **“将服务器控件绑定到数据源”** 一节时,我忍不住想寻找另一种在网页上导航和控制记录的方法。
这可能在挑战方面是个好主意,但在实际的 Web 开发方面则不一定。一句古老的谚语说:**“条条大路通罗马”**,有人可能会选择最近的路,而另一些人可能会选择人们通常走的路。我在这里所做的只是为了挑战和乐趣。因此,我选择了自己的道路,冒着迷路的风险(哈哈)。
背景
与允许用户计算机通过客户端数据库驱动程序直接与数据库服务器通信的 Windows 应用程序不同,基于 Web 的应用程序没有这样的东西。来自用户通过浏览器发出的任何请求都必须发送到 Web 服务器,然后 Web 服务器将该请求传递给数据库服务器。如果数据库服务器发现收到的请求有效(在查询语法、权限、存储过程名称方面),它将执行该请求,然后将结果发送给 IIS 以便中继到客户端浏览器。
在此,我想用另一种方法创建一个界面,使 Web 用户能够导航和控制记录。我想摆脱数据绑定,基本上只使用几个简单的 SQL 查询来达到这个目的。在外观上,它将与使用数据绑定的其他方法看起来没有区别。
我们需要的类
考虑到 C# 是一种完全支持面向对象编程的编程语言,我决定创建一个类,该类包含使用户能够导航和控制记录的所有必需方法和属性。因此,该类必须遵循以下要求:
- 它有一个输入属性,允许用户根据给定的主键值移动到特定记录,同时它会告知用户当前的主键值是多少。
public long id { get { return p_id; } set { p_id = value; if(p_id==-1) p_id = GetMaxID(); } }
在上面的代码中,您看到了
GetMaxID()
。这是一个私有函数,用于查找表中当前最后一条记录的位置。试想一下,当 Web 页面第一次在客户端浏览器上加载时,它必须显示表中的最后一条记录。但是表中的最后一条记录总是在变化,尤其是在其他用户添加新记录或删除现有记录时。因此,为了知道此刻哪条记录是最后一条记录,程序必须查找表中当前可用的主键值中的最大值,如以下代码所示:private long GetMaxID() { OleDbCommand p_com; OleDbDataReader p_reader; string p_query; long p_temp; p_query = "select max(id) as id from guest"; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); p_reader = p_com.ExecuteReader(); try { p_reader.Read(); p_temp = p_reader.GetInt32(0); } catch(Exception e) { p_temp = -1; } p_reader.Close(); return p_temp; }
关键在于查询。它使用了
Max
,这是 MS SQL 的一个聚合函数,用于过滤表中所有可用的主键值并获取最大的那个。正如您所注意到的,在我创建的 Guest.mdb 中,有一个名为 Guest 的表。该表的主键是id
。它具有数字数据类型并且是自动递增的。自动递增意味着每次添加新记录时,该新记录的主键值都等于前一条记录的最大主键值加 1。 - 它还有其他四个属性,用于返回主键值。第一个属性将返回表中第一条记录的主键值。第二个属性返回当前记录之前的记录的主键值。第三个属性返回当前记录之后的记录的主键值。最后一个属性是表中最后一条记录的主键值。
public long first { get { return p_first; } } public long before { get { return p_before; } } public long after { get { return p_after; } } public long last { get { return p_last; } }
p_first
、p_after
、p_before
和p_last
是Guest
类中的私有变量。它们公开了其他主键值,这些值随后将被导航按钮使用,包括前面讨论过的GetMaxID()
。我们再次使用GetMaxID()
来获取表中当前可用的最高主键值,该值指向表中最后一条记录的位置。public void Get() { ..... p_first = GetMinID(); p_before = GetBeforeID(); p_after = GetAfterID(); p_last = GetMaxID(); ..... }
Get
是Guest
的方法之一,它通过将这些变量赋值给四个函数来传递主键值给那些局部变量。private long GetBeforeID() { OleDbCommand p_com; OleDbDataReader p_reader; string p_query; long p_temp; p_query = "select top 1 id from guest where id < " + p_id.ToString() + " order by id desc"; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); p_reader = p_com.ExecuteReader(); if(p_reader.Read()) p_temp = p_reader.GetInt32(0); else p_temp = -1; p_reader.Close(); return p_temp; } private long GetAfterID() { OleDbCommand p_com; OleDbDataReader p_reader; string p_query; long p_temp; p_query = "select top 1 id from guest where id > " + p_id.ToString() + " order by id asc"; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); p_reader = p_com.ExecuteReader(); if(p_reader.Read()) p_temp = p_reader.GetInt32(0); else p_temp = -1; p_reader.Close(); return p_temp; } private long GetMinID() { OleDbCommand p_com; OleDbDataReader p_reader; string p_query; long p_temp; p_query = "select min(id) as id from guest"; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); p_reader = p_com.ExecuteReader(); if(p_reader.Read()) p_temp = p_reader.GetInt32(0); else p_temp = -1; p_reader.Close(); return p_temp; } private long GetMaxID() { OleDbCommand p_com; OleDbDataReader p_reader; string p_query; long p_temp; p_query = "select max(id) as id from guest"; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); p_reader = p_com.ExecuteReader(); try { p_reader.Read(); p_temp = p_reader.GetInt32(0); } catch(Exception e) { p_temp = -1; } p_reader.Close(); return p_temp; }
GetBeforeID
用于获取当前记录之前的记录的主键值。GetAfterID
用于获取当前记录之后的记录的主键值。GetMinID
用于获取表中第一条记录的主键值。而我不会讨论GetMaxID
,因为它前面已经提到过了。正如您所注意到的,它们的不同之处在于它们的 SQL 查询。
GetMinID
的操作与GetMaxID
相反。它查找最小的主键值。看看Min
,它也是 MS SQL 的一个聚合函数,其操作与Max
相反。GetBeforeID
用于查找当前记录之前的记录的主键值。它选择任何小于当前主键值的主键值,按降序排序,然后选择第一个。GetAfterID
的操作与GetBeforeID
相反。 - 仍然需要几个其他属性。它们必须返回选定记录的字段值。在此示例中,我创建了一个名为 guest.mdb 的小型 MDB 文件。它包含一个主键(名为
id
)和五个常规字段(firstname
、lastname
、company
、address
和job
)。public string firstname { get { return p_firstname; } set { p_firstname = value; } } public string lastname { get { return p_lastname; } set { p_lastname = value; } } public string company { get { return p_company; } set { p_company = value; } } public string address { get { return p_address; } set { p_address = value; } } public string job { get { return p_job; } set { p_job = value; } }
当用户执行更新或插入操作时,这些属性用于接受值。同时,当用户通过导航按钮向前或向后导航记录时,它们也提供选定记录的返回值。
con
属性用于将数据库连接传递到类中。此数据库连接是此类中所有操作使用的唯一连接。public OleDbConnection con { set { p_con = value; } }
- 最后两个属性用于返回错误号和错误描述。当操作过程中没有问题时,它将返回错误号 0 和空字符串作为错误描述。
public string ed { get { return p_ed; } } public long en { get { return p_en; } }
除了属性之外,该类还需要几个基本方法,这些方法必须暴露给用户以用于操作目的。因此,通过这些方法,用户可以添加新记录、更新现有记录或删除它们。
Add
方法。public void Add() { OleDbCommand p_com; string p_query; try { p_query = "insert into guest(firstname," + "lastname,company,address,job)"; p_query += "values('" + p_firstname + "','" + p_lastname + "','"; p_query += p_company + "','" + p_address + "','" + p_job + "')"; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); p_id = GetMaxID(); p_last = p_id; p_first = GetMinID(); p_before = GetBeforeID(); p_after = GetAfterID(); } catch(OleDbException n) { p_id = -1; p_en = 100; p_ed = n.ToString(); } }
Update
方法。public void Update() { OleDbCommand p_com; string p_query; try { p_query = "update guest set firstname='" + p_firstname + "',lastname='"; p_query += p_lastname + "',company='" + p_company + "',address='" + p_address; p_query += "',job='" + p_job + "' where id=" + p_id; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); } catch(OleDbException n) { p_en = 200; p_ed = n.ToString(); } }
Delete
方法。public void Delete() { OleDbCommand p_com; string p_query; try { p_query = "delete from guest where id=" + p_id; p_com = new OleDbCommand(p_query,p_con); p_com.ExecuteNonQuery(); if(GetBeforeID()!= -1) p_id = GetBeforeID(); else p_id = GetAfterID(); } catch(OleDbException n) { p_en = 300; p_ed = n.ToString(); } }
导航按钮
有四个按钮供用户导航记录。第一个按钮将用户带到表中的第一条记录。第二个按钮将用户带到当前显示记录之前的记录。第三个按钮将用户带到下一条记录。最后一个按钮将直接弹出表中最后一条记录。因为每个按钮都会使用户向前或向后遍历记录,所以我们需要保留当前信息,并在用户完全导航到目标记录后更新它。在此,我声明了几个隐藏变量来保存这些信息。
<input type="hidden" id="hdn_current" value="-1" runat="server">
<input type="hidden" id="hdn_first" runat="server">
<input type="hidden" id="hdn_before" runat="server">
<input type="hidden" id="hdn_after" runat="server">
<input type="hidden" id="hdn_last" runat="server">
我将 hdn_current
的默认值设置为 -1。当 Web 页面首次加载时使用此值。它将触发程序查找当前最高主键值,并将其分配给私有变量 p_id
。此更改也发生在 hdn_current
的值上。然后,程序使用此新值来获取其他四个维护每个导航按钮当前状态的隐藏值。
当用户单击按钮时,程序将触发与每个按钮相关的函数。
...
...
void f_first(Object o,EventArgs e)
{
f_display(Convert.ToInt32(hdn_first.Value));
}
void f_before(Object o,EventArgs e)
{
f_display(Convert.ToInt32(hdn_before.Value));
}
void f_after(Object o,EventArgs e)
{
f_display(Convert.ToInt32(hdn_after.Value));
}
void f_last(Object o,EventArgs e)
{
f_display(Convert.ToInt32(hdn_last.Value));
}
...
...
<asp:Button id="btn_first" Text="<<" Runat="server" Width="40"
Font-Name="Arial" Font-Size="12pt" Font-Bold
OnClick="f_first"/>
<asp:Button id="btn_before" Text="<" Runat="server" Width="40"
Font-Name="Arial" Font-Size="12pt" Font-Bold
OnClick="f_before"/>
<asp:Button id="btn_after" Text=">" Runat="server" Width="40"
Font-Name="Arial" Font-Size="12pt" Font-Bold
OnClick="f_after"/>
<asp:Button id="btn_last" Text=">>" Runat="server" Width="40"
Font-Name="Arial" Font-Size="12pt" Font-Bold
OnClick="f_last"/>
...
f_display
是一个函数,它将根据给定的主键值弹出选定记录的信息。然后,它会将信息附加到 Web 表单上的 Web 控件上。导航按钮会使用它。它们的 Click
事件将导致此函数被执行。
void f_display(long id)
{
Guest g = new Guest(objCon);
g.id = id;
g.Get();
if(g.en==0)
{
txt_firstname.Text = g.firstname;
txt_lastname.Text = g.lastname;
txt_company.Text = g.company;
txt_address.Text = g.address;
f_get_job_index(lst_position,g.job);
hdn_current.Value = g.id.ToString();
hdn_first.Value = g.first.ToString();
hdn_before.Value = g.before.ToString();
hdn_after.Value = g.after.ToString();
hdn_last.Value = g.last.ToString();
btn_update.Enabled = true;
btn_delete.Enabled = true;
if(g.after==-1)
{
btn_after.Enabled = false;
btn_last.Enabled = false;
}
else
{
btn_after.Enabled = true;
btn_last.Enabled = true;
}
if(g.before==-1)
{
btn_before.Enabled = false;
btn_first.Enabled = false;
}
else
{
btn_before.Enabled = true;
btn_first.Enabled = true;
}
}
else
{
txt_firstname.Text = "";
txt_lastname.Text = "";
txt_company.Text = "";
txt_address.Text = "";
lst_position.SelectedIndex = 0;
btn_first.Enabled = false;
btn_before.Enabled = false;
btn_after.Enabled = false;
btn_last.Enabled = false;
hdn_current.Value = "-1";
hdn_first.Value = "-1";
hdn_before.Value = "-1";
hdn_after.Value = "-1";
hdn_last.Value = "-1";
btn_update.Enabled = false;
btn_delete.Enabled = false;
}
}
控件按钮
只有三个按钮允许用户添加新记录、更新选定的现有记录或删除一条记录。其中每个按钮都与三个服务器端函数之一相关联。这些函数只需实例化我们之前创建的 Guest
类并相应地使用每个公开的方法。
这三个函数也暴露了错误消息,以防发生错误。在发生错误或操作不成功时向用户提供恰当的消息非常重要。
void f_add(Object o,EventArgs e)
{
if(btn_add.Text == "Add New")
{
btn_add.Text = "Save";
btn_first.Enabled = false;
btn_before.Enabled = false;
btn_after.Enabled = false;
btn_last.Enabled = false;
btn_update.Text = "Cancel";
btn_update.Enabled = true;
btn_delete.Enabled = false;
txt_firstname.Text = "";
txt_lastname.Text = "";
txt_company.Text = "";
txt_address.Text = "";
lst_position.SelectedIndex = 0;
}
else
{
Guest g = new Guest(objCon);
g.firstname = txt_firstname.Text;
g.lastname = txt_lastname.Text;
g.company = txt_company.Text;
g.address = txt_address.Text;
g.job = lst_position.SelectedItem.Value;
g.Add();
if(g.en!=0) strMsg = g.ed;
f_display(g.id);
btn_add.Text = "Add New";
btn_update.Text = "Update";
btn_update.Enabled = true;
btn_delete.Enabled = true;
}
}
void f_update(Object o,EventArgs e)
{
if(btn_update.Text=="Update")
{
Guest g = new Guest(objCon);
g.id = Convert.ToInt32(hdn_current.Value);
g.firstname = txt_firstname.Text;
g.lastname = txt_lastname.Text;
g.company = txt_company.Text;
g.address = txt_address.Text;
g.job = lst_position.SelectedItem.Value;
g.Update();
if(g.en!=0) strMsg = g.ed;
}
else
{
btn_update.Text="Update";
btn_add.Text="Add New";
f_display(Convert.ToInt32(hdn_current.Value));
}
}
void f_delete(Object o,EventArgs e)
{
Guest g = new Guest(objCon);
g.id = Convert.ToInt32(hdn_current.Value);
g.Delete();
if(g.en==0) f_display(g.id); else strMsg = g.ed;
}
最后的话
也许,你们中的许多人会认为我的做法效率不高。他们可能更喜欢使用数据绑定而不是我刚才所做的。我不会否认他们的理由。但尝试一些不同的东西让我感觉很好!