在ASP.NET对于SQL Server数据库的增删改查自然是便利的,ASP.NET提供了大量封装好的数据库表格,例如GridView什么的,但这些组件只能通过VS自带的属性进行样式的修改,远不如类似与其它语言的循环结构foreach的Repeater,能够自己在“源”代码中的修改遍历,当然这个组件并不是这么好驾驭是真的。
下面有一个例子说明这个组件的运用。
在数据库test中有一张用烂的user_info表,结构如下:
建表语句如下:
CREATE TABLE [dbo].[user_info]( [id] [int] IDENTITY(1,1) NOT NULL, [username] [varchar](50) NULL, [password] [varchar](50) NULL, PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY]在Default.aspx页面,完成对这个表的增删改查操作。
要求不能出现相同的用户名。
解决方案的目录结构如下所示:
其中DB.cs是《【C#】利用C#窗体与SQL Server的连接、Treeview制作SQL Server数据库查看器》(点击打开链接)中介绍过的数据库查询类,窗体程序与ASP.NET是通用的,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data;//DataTable用到 using System.Data.SqlClient;//一系列的数据库操作类用到 using System.Configuration; namespace Repater { class DB : IDisposable { private SqlConnection sqlConnection; // 以下代码,保证该类只能有一个实例 // 在自己内部定义自己的一个实例,只供内部调用 private static DB db = null; // 这个类必须自动向整个系统提供这个实例对象 // 这里提供了一个供外部访问本class的静态方法,可以直接访问 public static DB getInstance() { if (db == null) { db = new DB(); } return db; } private DB()// 私有无参构造函数 { sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["myCon"].ToString()); sqlConnection.Open(); } //单例化结束 public DataTable getBySql(string sql, Object[] param) {//查询 sql = String.Format(sql, param);//用字符串参数替换的形式防止注入 SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(new SqlCommand(sql, sqlConnection)); DataTable dataTable = new DataTable(); sqlDataAdapter.Fill(dataTable); return dataTable; } public DataTable getBySql(string sql) {//无参数的查询 SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(new SqlCommand(sql, sqlConnection)); DataTable dataTable = new DataTable(); sqlDataAdapter.Fill(dataTable); return dataTable; } public void setBySql(string sql, Object[] param) { //无查询结果的修改 sql = String.Format(sql, param);//用字符串参数替换的形式防止注入 new SqlCommand(sql, sqlConnection).ExecuteNonQuery(); } public void setBySql(string sql) { //无参数,无查询结果的修改 new SqlCommand(sql, sqlConnection).ExecuteNonQuery(); } public void Dispose() {//相当于析构函数 sqlConnection.Close(); //在C#中关闭数据库连接不能在类的析构函数中关,否则会抛“内部 .Net Framework 数据提供程序错误 1”的异常 //通过实现C#中IDisposable接口中的Dispose()方法主要用途是释放非托管资源。 } } }
<connectionStrings> <add name="myCon" connectionString="server=.\SQLEXPRESS;database=test;Trusted_Connection=SSPI;" providerName="using System.Data.SqlClient;"/> </connectionStrings>
之后,先不急着在Default.aspx.cs中编程,先修改源视图中的代码,也就是Default.aspx,具体如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Repater._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Repeater ID="Repeater1" runat="server" OnItemCommand="Repeater1_ItemCommand"> <HeaderTemplate><%--不参与循环的头部--%> <table border="1"> <tr> <th> 用户名 </th> <th> 密码 </th> <th> 操作 </th> </tr> </HeaderTemplate><%--循环产生的部分,被绑定的数据也就是DataTable有多少行,这里就会循环多少次--%> <ItemTemplate> <tr> <td><%--输出数据,利用到<%#Eval("username")%>,里面的任何一个符号,主要是双引号是不能改变的--%> <asp:TextBox ID="TextBox1" runat="server" Text='<%#Eval("username")%>' /> </td><%--因此如果出现如上的引号嵌套,仅能将外面的引号改成单引号--%> <td> <asp:TextBox ID="TextBox2" runat="server" Text='<%#Eval("password")%>' /> </td> <td><%--这里,CommandName,CommandArgument都是传给OnItemCommand="Repeater1_ItemCommand"的参数--%> <asp:Button runat="server" CommandName="update" CommandArgument='<%#Eval("id")+","+(Container as RepeaterItem).ItemIndex%>' Text="修改" /><%--修改按钮比较复杂,除了在CommandName传递一个update给后台,标识自己是修改,还要同时传递两个参数一个打印的id,与该行的行数用于发现TextBox--%> <asp:Button runat="server" CommandName="del" CommandArgument='<%#Eval("id")%>' Text="删除" OnClientClick='return confirm("确定此操作吗?")' /> </td> </tr> </ItemTemplate> <FooterTemplate><%--不参与循环的尾部--%> </table> </FooterTemplate> </asp:Repeater> <b>插入用户:</b><br /><%--这里没啥好说的,就一个asp.net中常见的表单--%> 用户名:<asp:TextBox ID="TextBox1" runat="server" />密码:<asp:TextBox ID="TextBox2" runat="server" /> <asp:Button runat="server" ID="Button1" Text="添加" OnClick="Button1_Click" /> </div> </form> </body> </html>
同时,如果你要输出绑定数据的同时,同时带上其它数据的输出,那你仅能类似这种形式搞了:CommandArgument='<%#Eval("id")+","+(Container as RepeaterItem).ItemIndex%>'这也没办法,ASP.NET是这样规定的。
也可以看到Repeater的代码类似其它语言的foreach,你可以自由地修改里面的输出形式,可以是table,可以是div等等
之后在后端Default.aspx.cs你就这样写,配合Default.aspx完成SQL Server数据库的增删改查:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace Repater { public partial class _Default : System.Web.UI.Page { DB db; protected void Page_Load(object sender, EventArgs e) { db = DB.getInstance();//必须放在!Page.IsPostBack外面,因为每次离开~加载这个页面,伴随这数据库实例被销毁,你必须将其重新初始化 if (!Page.IsPostBack)//必须有,规定数据不能多次被绑定。 { Repeater1.DataSource = db.getBySql("select * from [user_info]"); Repeater1.DataBind();//直接通过绑定来完成Repeater的输出 } } protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e) { if (e.CommandName == "update")//如果点击的是被标记为CommandName="update"的按钮,也就是修改按钮 { int id = int.Parse(e.CommandArgument.ToString().Split(',')[0]);//这里还真必须用单引号来表示字符,而不是""的字符串~,C#的Split就一个以字符,而不是字符串参数的代码 int itemIndex = int.Parse(e.CommandArgument.ToString().Split(',')[1]);//藏在CommandArgument='<%#Eval("id")+","+(Container as RepeaterItem).ItemIndex%>'逗号后面的参数就是该行行号 TextBox TextBox1 = Repeater1.Items[itemIndex].FindControl("TextBox1") as TextBox;//获得改行的TextBox1 TextBox TextBox2 = Repeater1.Items[itemIndex].FindControl("TextBox2") as TextBox;//获得改行的TextBox2 //这里是修改数据库表的一般逻辑,不赘述了 if (TextBox1.Text.Trim().Equals("") || TextBox2.Text.Trim().Equals("")) { Response.Write("<b>用户名,密码不得为空!</b>"); } else { if (db.getBySql("select * from [user_info] where [username]='{0}'", new Object[] { TextBox1.Text }).Rows.Count == 0)//如果没有这个用户名才能修改 { db.setBySql("update [user_info] set [username]='{0}' where [id]={1}", new Object[] { TextBox1.Text, id }); db.setBySql("update [user_info] set [password]='{0}' where [id]={1}", new Object[] { TextBox2.Text, id }); //数据绑定并不意味着会自动刷新Repeater1,必须自己再用代码,刷新一下Repeater1 Repeater1.DataSource = db.getBySql("select * from [user_info]"); Repeater1.DataBind(); Response.Write("<b>已修改!</b>"); } else { Response.Write("<b>已有该用户名!</b>"); } } } if (e.CommandName == "del")//删除按钮 { db.setBySql("delete from [user_info] where [id]=" + e.CommandArgument);//e.CommandArgument就是藏着该项的数据库id //刷新Repeater1 Repeater1.DataSource = db.getBySql("select * from [user_info]"); Repeater1.DataBind(); Response.Write("<b>已删除!</b>"); } } protected void Button1_Click(object sender, EventArgs e) { //添加按钮 if (TextBox1.Text.Trim().Equals("") || TextBox2.Text.Trim().Equals("")) { Response.Write("<b>用户名,密码不得为空!</b>"); } else { if (db.getBySql("select * from [user_info] where [username]='{0}'", new Object[] { TextBox1.Text }).Rows.Count == 0) { db.setBySql("insert into [user_info]([username],[password]) values('{0}','{1}')", new Object[] { TextBox1.Text, TextBox2.Text }); //刷新Repeater1 Repeater1.DataSource = db.getBySql("select * from [user_info]"); Repeater1.DataBind(); Response.Write("<b>添加成功!</b>"); } else { Response.Write("<b>已有该用户名!</b>"); } } } } }
可以看到,这里怎么通过CommandName与CommandArgument操纵在Repeater的按钮。
需要注意的是,在Page_Load方法中,Repeater的数据绑定操作,必须放在if (!Page.IsPostBack){}之中,否则会出现如下的错误: