ADO.Net基础应用简介

一、每次读取数据都创建连接、执行Command得到SqlDataReader太麻烦,让我们封装出一个SQLHelper类吧,代码如下所示!

首先,还是在Program.cs类的Main()方法中添加如下代码:

View Code
string dataDir = AppDomain.CurrentDomain.BaseDirectory;
if (dataDir.EndsWith(@"\bin\Debug\")
|| dataDir.EndsWith(@"\bin\Release\"))
{
dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;
AppDomain.CurrentDomain.SetData("DataDirectory", dataDir);
}

其次,再添加一个应用程序配置文件:App.config,其所有代码如下:

View Code
 1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <configSections>
4 </configSections>
5 <connectionStrings>
6 <add name="ConnStr" connectionString="Data Source=.\SQLEXPRESS;AttachDBFilename=|DataDirectory|\Test.mdf;&#xD;&#xA; Integrated Security=True;Connect Timeout=30;User Instance=True" />
7 <add name="尝试封装.Properties.Settings.TestConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Test.mdf;Integrated Security=True;User Instance=True"
8 providerName="System.Data.SqlClient" />
9 </connectionStrings>
10 </configuration>

再次,添加一个SQLHelper类:(用于被其它代码调用,以下为该类的完整代码)

该类中含有四个方法,分别是:ExecuteNonQuery(string sql, params SqlParameter[] parameters)、ExecuteScalar(string sql, params SqlParameter[] parameters)、ExecuteReader(string sql, params SqlParameter[] parameters)和ExecuteDataTable(string sql, params SqlParameter[] parameters)。

在封装这个类之前称简单介绍几点:
a、封装一个SQLHelper类方便使用,提供ExecuteNonQuery(string sql, params SqlParameter[] parameters);ExecuteScalar(string sql, params SqlParameter[] parameters);ExecuteReader(string sql, params SqlParameter[] parameters);ExecuteDataTable(string sql, params SqlParameter[] parameters)等方法,网上有微软提供的最全的SQLHelper类,是Enterprise Library中的一部分。
b、用SQLHelper重写登录程序
c、new SqlParameter("e",0)的陷阱
d、sqlconnection在程序中一直保持它open可以吗?对于数据库来说,连接是非常宝贵的资源,一定要用完不close或dispose。

View Code
 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Data.SqlClient;
6 using System.Configuration;
7 using System.Data;
8
9 namespace 尝试封装
10 {
11 //优点:一、把数据库连接代码都放在SQLHelper中,使代码更简洁
12 //二、使用DataTable可以随意读取数据库,而之前做的用户登录使用的SqlReader只能逐行往前读
13 class SQLHelper
14 {
15 public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters)
16 {
17 string connStr=ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
18 using (SqlConnection conn = new SqlConnection(connStr))
19 {
20 conn.Open();
21 using (SqlCommand cmd = conn.CreateCommand())
22 {
23 cmd.CommandText = sql;//清除旧数据
24 foreach (SqlParameter parameter in parameters)
25 {
26 cmd.Parameters.Add(parameter);
27 }
28 return cmd.ExecuteNonQuery();//对连接执行SQL语句并返回受影响的行数
29 }
30 }
31 }
32 public static object ExecuteScalar(string sql, params SqlParameter[] parameters)
33 {
34 string connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
35 using (SqlConnection conn = new SqlConnection(connStr))
36 {
37 conn.Open();
38 using (SqlCommand cmd = conn.CreateCommand())
39 {
40 cmd.CommandText = sql;
41 foreach (SqlParameter parameter in parameters)
42 {
43 cmd.Parameters.Add(parameter);
44 }
45 //执行查询,并返回查询所返回的结果集中第一行的第一列。忽略其它的行或列。
46 return cmd.ExecuteScalar();
47 }
48 }
49 }
50 public static SqlDataReader ExecuteReader(string sql, params SqlParameter[] parameters)
51 {
52 string connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
53 using (SqlConnection conn = new SqlConnection(connStr))
54 {
55 conn.Open();
56 using (SqlCommand cmd = conn.CreateCommand())
57 {
58 cmd.CommandText = sql;
59 foreach (SqlParameter parameter in parameters)
60 {
61 cmd.Parameters.Add(parameter);
62 }
63 //执行查询,并返回查询所返回的结果集中第一行的第一列。忽略其它的行或列。
64 return cmd.ExecuteReader();
65 }
66 }
67 }
68 public static DataTable ExecuteDataTable(string sql, params SqlParameter[] parameters)
69 {
70 string connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
71 using (SqlConnection conn = new SqlConnection(connStr))
72 {
73 conn.Open();
74 using (SqlCommand cmd = conn.CreateCommand())
75 {
76 cmd.CommandText = sql;
77 foreach (SqlParameter parameter in parameters)
78 {
79 cmd.Parameters.Add(parameter);
80 }
81 //执行查询,并返回查询所返回的结果集中第一行的第一列。忽略其它的行或列。
82 DataSet dataset = new DataSet();
83 SqlDataAdapter adapter = new SqlDataAdapter(cmd);
84 adapter.Fill(dataset);
85 return dataset.Tables[0];
86 }
87 }
88 }
89 }
90 }

最后,练习一个简单用户登录验证程序:使用到上面SQLHelper.cs类中的ExecuteDataTable(string sql, params SqlParameter[] parameters)方法。程序简单分析如下:

界面中有两个文本框:分别用于让用户输入用户名和密码;当用户单击登录按钮btnLogin时,验证用户名及密码是否正确。具体过程请看代码:

View Code
 1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9 using System.Data.SqlClient;
10 using System.Configuration;
11 using 尝试封装.DataSet1TableAdapters;
12
13 namespace 尝试封装
14 {
15 public partial class Form1 : Form
16 {
17 public Form1()
18 {
19 InitializeComponent();
20 }
21
22 private void btnLogin_Click(object sender, EventArgs e)
23 {
24 DataTable dt = SQLHelper.ExecuteDataTable("select * from T_Users where UserName=@UserName",
25 new SqlParameter("UserName", tBox_Name.Text));
26 if (dt.Rows.Count <= 0)
27 {
28 MessageBox.Show("用户名不存在!");
29 return;
30 }
31 else
32 {
33 DataRow row = dt.Rows[0];//要返回的行是从零开始的索引
34 int errorTimes = Convert.ToInt32(row["ErrorTimes"]);
35 if (errorTimes >= 3)
36 {
37 MessageBox.Show("登录的错误次数过多!");
38 return;
39 }
40 //取出数据库中的密码
41 string dbPassword = Convert.ToString(row["Password"]);//row返回的索引是object类型,因此要转换成字符串
42 if (dbPassword == tBox_Pwd.Text)
43 {
44 MessageBox.Show("恭喜您,登录成功!");
45 //密码出错一次,将错误次数置0
46 SQLHelper.ExecuteNonQuery("Update T_Users Set ErrorTimes=0 where UserName=@UserName",
47 new SqlParameter("UserName", tBox_Name.Text));//登录成功则将错误次数变为0
48 }
49 else
50 {
51 //密码出错一次,将错误次数加1
52 SQLHelper.ExecuteNonQuery("Update T_Users Set ErrorTimes=ErrorTimes+1 where UserName=@UserName",
53 new SqlParameter("UserName", tBox_Name.Text));
54 MessageBox.Show("密码错误!");
55 }
56 }
57 }
58 }
59 }

 

二、使用SqlDataReader有以下两个优点:
1、SqlDataReader是连接相关的,SqlDataReader中的查询结果并不是放到程序中的,而是放在数据库服务器中,SqlDataReader只是相当于放了一个指针(游标),只能读取当前游标指向的行,一旦连接断开就不能再读取。这样做的好处就是无论查询结果有多少条,对程序占用的内存都几乎没有影响。
2、SqlDataReader对于小数据来说 ADO.Net中提供了数据集的机制,将查询结果填充到本地内存中,这样连接断开、服务器断开都不影响数据的读取。
在窗体上拖一个按钮,把Text属性改为ExecuteReader,其单击事件代码如下所示:

View Code
private void ExecuteReader_Click(object sender, EventArgs e)
{
SqlDataReader reader = SQLHelper.ExecuteReader("select * from T_Persons");
while (reader.Read())
{
string name = reader.GetString(reader.GetOrdinal("Name"));
MessageBox.Show(name);
}
}

以上代码对应上面SQLHelper.cs类中的ExecuteReader(string sql, params SqlParameter[] parameters)方法。

三、再添加以下七个按钮,其Text属性分别命名为:ExecuteNonQuery、ExecuteScalar、ExecuteDataTable、陷阱、DataSet测试、修改DataSet、测试强类型DataSet。(后面三个练习用到了数据集DataSet,而没用SQLHelper封装的类)
现在分别对上面七个按钮练习:
1、ExecuteNonQuery单击事件如下:(运行结果是将表的记录增加一条)

View Code
    private void ExecuteNonQuery_Click(object sender, EventArgs e)
{
SQLHelper.ExecuteNonQuery("Insert into T_Persons(Name,Age)values(@Name,@Age)", new SqlParameter
("Name", "Logan"), new SqlParameter("Age", 20));
}

执行上面的事件时,应用的是SQLHelper类中的ExecuteNonQuery(string sql, params SqlParameter[] parameters)

2、ExecuteScalar单击事件如下:(以下代码的功能是显示表中的记录数目)

View Code
private void ExecuteScalar_Click(object sender, EventArgs e)
{
object i = SQLHelper.ExecuteScalar("select count(*) from T_Persons");
MessageBox.Show(Convert.ToString(i));
}

执行上面的事件时,应用到了SQLHelper类中的ExecuteScalar(string sql, params SqlParameter[] parameters)方法

3、ExecuteDataTable单击事件如下:(以下代码的功能是将表中的所有用户名逐个显示出来)

View Code
private void ExecuteDataTable_Click(object sender, EventArgs e)
{
//DataTable不适合存放太多数据
DataTable dt = SQLHelper.ExecuteDataTable("select * from T_Persons");
for (int i = 0; i < dt.Rows.Count; i++)
{
DataRow row = dt.Rows[i];
string name = Convert.ToString(row["Name"]);
MessageBox.Show(name);
}
}

执行上面的事件时,应用到了SQLHelper类中的ExecuteDataTable(string sql, params SqlParameter[] parameters)方法

4、添加一个容易出现“陷阱”的按钮Trap,代码如下:

View Code
private void Trap_Click(object sender, EventArgs e)
{
//实参为0与不为0时,这两种情况下将光标放在SqlParameter中按F12转到定义就出现不同的重载函数的情况,非常诡异类型转换陷阱!
SQLHelper.ExecuteDataTable("select * from T_Users where Id=@Id",new SqlParameter("Id",(object)0));//修改方法是在0前加上object
}

5、DataSet测试的单击事件代码如下:(测试断开与数据库的连接也不会影响数据的读取,代码的功能也是将表中所有用户名逐个显示)

View Code
 1 private void Test_Click(object sender, EventArgs e)
2 {
3 //string connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
4 //using (SqlConnection conn = new SqlConnection(connStr))
5 //{
6 // conn.Open();
7 // using (SqlCommand cmd = conn.CreateCommand())
8 // {
9 // cmd.CommandText = "select * from T_Persons";
10 // DataSet dataset = new DataSet();
11 // SqlDataAdapter adapter = new SqlDataAdapter(cmd);
12 // adapter.Fill(dataset);
13 // DataTable table=dataset.Tables[0];//取到了上面执行结果的一个表
14 // for (int i = 0; i < table.Rows.Count; i++)
15 // {
16 // DataRow row = table.Rows[i];
17 // string name = Convert.ToString(row["Name"]);
18 // MessageBox.Show(name);
19 // }
20 // }
21 //}
22 //测试断开与数据库的连接也不会影响数据的读取
23 DataSet dataset = new DataSet();
24 string connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
25 using (SqlConnection conn = new SqlConnection(connStr))
26 {
27 conn.Open();
28 using (SqlCommand cmd = conn.CreateCommand())
29 {
30 cmd.CommandText = "select * from T_Persons";
31 SqlDataAdapter adapter = new SqlDataAdapter(cmd);
32 adapter.Fill(dataset);
33 }
34 }
35 DataTable table = dataset.Tables[0];//取到了上面执行结果的一个表
36 for (int i = 0; i < table.Rows.Count; i++)//遍历每一行
37 {
38 DataRow row = table.Rows[i];
39 string name = Convert.ToString(row["Name"]);
40 MessageBox.Show(name);
41 }
42 }

6、修改DataSet单击事件如下:(代码的功能是修改用户名)
首先,简单讲下DataSet的更新:
1)可以更新行row["Name"]="Dorothy"、删除行datatable.Rows.Remove()、新增行datatable.NewRow()。这一切都是修改的内存中的DataSet,并没有修改数据库。
2)可以调用SqlDataAdapter的Update方法将对DataSet的修改提交到数据库,Update方法有很多重载方法,可以提交整个DataSet、DataTable

或者若干DataRow。但是需要为SqlDataAdapter提供DeleteCommand、UpdateCommand、InsertCommand它才知道如何将对DataSet的修改提交到

数据库,由于这几个Command要求的格式非常苛刻,因此开发人员自己写非常困难;可以用SqlCommandBuilder自动生成这几个Command,用法很

简单:new SqlCommandBuilder(adapter)。查看生成的Command(没有直接赋值给SqlDataAdapter,看SqlCommandBuilder的)。

SqlCommandBuilder要求表必须有主键。
3)通过DataRow的RowState可以获得行的状态(删除、修改、新增等);调用DataSet的GetChanges()方法得到变化的结果集,降低传递的资源

占用。

View Code
 1 private void Alter_Click(object sender, EventArgs e)
2 {
3 DataSet dataset = new DataSet();
4 string connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ConnectionString;
5 using (SqlConnection conn = new SqlConnection(connStr))
6 {
7 conn.Open();
8 using (SqlCommand cmd = conn.CreateCommand())
9 {
10 cmd.CommandText = "select * from T_Users";
11 SqlDataAdapter adapter = new SqlDataAdapter(cmd);
12 adapter.Fill(dataset);
13 DataTable table = dataset.Tables[0];
14 DataRow row = table.Rows[0];
15 row["UserName"] = "Dorothy";
16 table.Rows.RemoveAt(1);// 从集合中移除指定索引处的行
17 DataRow dr = table.NewRow();//新建行
18 //在调试时右击builder打开快速监视窗口并执行builder.GetUpdateCommand()表达式,可以查看文本可视化工具
19 SqlCommandBuilder builder = new SqlCommandBuilder(adapter);//SqlCommandBuilder可以自动生成一些必要的Command命令
20 adapter.Update(dataset);
21 }
22 }
23 }

7、测试强类型DataSet单击事件(先简单介绍几点)
1)、常用的语句:DataSet dataset=new DataSet();
SqlDataAdapter adapter=new SqlDataAdapter(cmd);
adapter.Fill(dataset);
2)  SqlDataAdapter是DataSet和数据库之间沟通的桥梁。
3)  数据集DataSet包含若干表DataTable,DataTable包含若干行DataRow。foreach(DataRow row in dataset.Tables[0].Rows)row["Name"];
添加以下代码前,还要添加一个T_UsersRow.cs类和一个数据集DataSet1.xsd文件,然后打开数据集,将T_Users表拖到数据集中:

View Code
 1 private void 强类型DataSet_Click(object sender, EventArgs e)
2 {
3 T_UsersTableAdapter adapter = new T_UsersTableAdapter();
4 尝试封装.DataSet1.T_UsersDataTable data = adapter.GetData();
5 for (int i = 0; i < data.Count; i++)
6 {
7 尝试封装.DataSet1.T_UsersRow userRow = data[i];
8 MessageBox.Show(userRow.UserName);
9 }
10 }

代码简单解析:1、含有两个带表名的类:分别是"表名+DataTable"和"表名+ Row";2、代码的功能是将表中的用户名逐个显示出来。

个人学习笔记,难免有漏洞,请您在评论中指点,谢谢!

posted on 2011-12-11 23:19  Gelivable【IT随笔】  阅读(450)  评论(0编辑  收藏  举报

导航