ADO.NET实例教学一
今天我们先把上次文章《ADO.NET(内涵效率问题)》 中给大家讲到的问题简单的回顾一下.
在ADO.NET中除了可以连接SQL数据库的类,还有哪些类呢?我们用反编译器看一下吧。
一、ADO中的DbConnection类
二、DbConection的三个子类
三、DbCommand类
四、给我们写代码时带来的思路
好,ado,net就是访问数据库的一种技术。
在访问数据库的时候至少要用到哪些类呢?
1是SqlConnection,指定它连接哪个数据库的叫:“连接字符串”。连接哪个数据库,哪个服务器,具体的登录方式,还有一些像操作时间的属性。
2.我们该执行SQl命令了。
这一步里我们要:确定SQL语句。
3.接下来我们该执行了,执行的时候需要
创建SqlCommand
4.SqlCommand有好几个方法呢?
建议执行不同SQL语句的时候,使用不同的方法。那么我们有几个方法分别是什么时候用来着?
ExecuteNonQuery() 执行对数据库的增删改,返回受影响的行数,适合:insert、delete、update(对于其他语句返回-1)----------这个方法有返回值,返回值是个整型。返回所影响的行数。返回的如果是0,不能证明执行失败了。
理由:现在我要把班级表中所有的男同学的年龄改成20,但是表中都是女生,返回所影响的行数是0。这个时候我们不能说SQL语句执行失败了。
除了这个还有
ExecuteScalar()执行查询,返回首行首列
有返回值,return:object
一般用来执行什么样的SQL语句的呢?
用来执行返回单个值的sql语句.
ExecuteReader(); 当执行sql语句返回多行多列时使用。
返回SqlDataReader 通过reader拿到数据
其实这三个方法执行任意一条sql语句都能成功,只是我们用对的时候能得到那个返回结果,用错的就没法得到这个返回结果了。
接下来我们重点说下reader的使用。reader取数据时是个什么过程呢?
执行完sql语句以后,查询出来数据在服务器那个内存里面。判断下如果没数据告知用户,要是有数据就一行一行的读出来。通过索引,获取当前行的某一列的值。
需要注意:在reader的使用的时候必须保证Connection是打开着的。reader当然更得是打开着的。
当reader使用完以后也需要close关闭。
遇到空值的时候用IsDBNull
我们还学习了ado.net连接池
连接池默认是启用的,dot.net程序的连接池。程序一关闭就自动关闭了。
连接池的作用是什么呢?
可以提升程序的性能。
为什么能提升程序的性能呢?原因就是始终不关闭。
1>当上一个连接对象调用Close()方法后,才会将连接放入池中。
2>当再次创建连接对象时,只有当本次创建连接对象所使用的连接字符串与池中现有连接对象的连接字符串完全一致时,才会使用池中的连接对象。
还学了带参数的sql语句:
它的作用是,防止注入攻击。
等咱们后面学了存储过程之后,你会发现,带参数的sql语句编译之后,调的就是存储过程。是完全一样的。
所以以后不要遇到这个问题,带参数的sql不能防止注入攻击,只有存储过程可以。
实际上,两个问题是一样的,都能防止注入攻击。
我以前有个同学去面试,面试官说只有存储过程能防止注入攻击。我同学说:“带参数的sql语句也可以防止啊”。面试官对此,相当不屑。我同学不知道,他俩说的是一回事儿啊。
带参数的sql语句写的时候跟之前几乎是一模一样的,只是
string sql=”select*from where id=@uid” 出现了一些@变量。
将来集合里面add几个对象,如:cmd.Parameters.Add(new SqlParameter(“@uid”,”zxh));
为了方便可以直接:cmd.Parameters.AddWithValue(“”,””);
建议大家以后就是用带参数的sql语句。
像select count(*) from Biao where id=;没有涉及到动态的拼接,就没必要使用带参数的sql语句了。
我们在写带参数的sql语句的时候sql server仅支持已命名参数@arg1,而oledb、odbc仅支持通用参数标记(?),不同数据提供程序对参数的写法可能不同。
下面我们做一些个练习:
V1.0:用户界面中进行登录判断。
V1.1:输错三次禁止登陆,1分钟后才能继续。用数据库记录ErrorTimes,最后出错时间lastErrorDateTime。。
数据导入:从文本文件导入用户信息。
数据导出:将用户信息导出到文本文件。
V1.1:输错三次禁止登陆,1分钟后才能继续。用数据库记录ErrorTimes,最后出错时间lastErrorDateTime。
五、画好登录界面
首先想下sql语句,或者在写程序之前先分析一下。
六、需求分析
所以,需要给这张表增加两列。
七、按照需求建好表
好,现在看下这个案例的基本思路:
出错之后就记录,记录一次时间,记录一次次数。
下次登录之前判断是否为锁定,如果锁定了,提示一下用户。
如果没锁定再判断下,是时间不够呢?还是次数不够?
八、校验当前用户是否被锁定的sql语句
九、登录及判断登录错误次数及延迟时间的代码
十、登录时间代码
介绍个小知识:
十一、获取时间问题
登录限制时间的代码:
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 11 namespace _9._5案例 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 /// <summary> 20 /// 点击登录按钮 21 /// </summary> 22 /// <param name="sender"></param> 23 /// <param name="e"></param> 24 private void btnLogin_Click(object sender, EventArgs e) 25 { 26 //1.获取用户名和密码 27 string loginId = txtUid.Text.Trim(); 28 string loginPwd = txtPwd.Text; 29 30 #region 每次登录之前执行校验 31 //4.每次登录之前执行校验,也需要连接数据库,封装成一个方法,写在里面。 32 bool b = IsLocked(loginId); 33 #endregion 34 //5.b返回true就是以锁定了。 35 if (b) 36 { 37 MessageBox.Show("您的账户已经锁定,请1分钟以后再尝试!"); 38 } 39 else 40 { 41 //6.如果用户没有锁定,有两种情况1>次数不够3次,2>时间已经超过1分钟 42 //"Update UserLogin set ErrorTimes=0 where LoginId=@uid and ErrorTimes>=3" 43 //大于三次清零 44 ClearErrorTime(loginId); 45 //回答一个问题就是这只在sql语句中清3次的情况? 46 //原因是我在之前登陆之前的校验(4)中已经判断了用户登录小于1分钟的情况。这样返回false的时候,说明登录时间已经超过1分钟了。 47 48 #region 执行登录校验 49 //2.进行登录操作 50 //写个连接字符串 51 string constr = "Data Source=HY-PC;Initial Catalog=itcastcn;Integrated Security=True"; 52 //连接对象 53 using (SqlConnection con = new SqlConnection(constr)) 54 { 55 //执行sql语句 56 string sql = "select count(*) from UserLogin where LoginId=@uid and LoginPwd=@pwd"; 57 //执行之前创建sqlcommand 58 using (SqlCommand cmd = new SqlCommand(sql, con)) 59 { 60 //增加参数 61 cmd.Parameters.AddWithValue("@uid", loginId); 62 cmd.Parameters.AddWithValue("@pwd", loginPwd); 63 //开始执行,执行之前先open,再close 64 con.Open(); 65 int r = Convert.ToInt32(cmd.ExecuteScalar()); 66 con.Close(); 67 //这如果不用带参数的sql语句,用r=1也可以避免注入攻击。 68 if (r > 0) 69 { 70 MessageBox.Show("登录成功!"); 71 } 72 else 73 { 74 //3.每次出错的时候都要记录一下错误次数与最后一次登录时间 75 WriteLoginLog(loginId); 76 MessageBox.Show("登录失败!"); 77 } 78 } 79 } 80 #endregion 81 } 82 } 83 private void ClearErrorTime(string loginId) 84 { 85 string constr = "Data Source=HY-PC;Initial Catalog=itcastcn;Integrated Security=True"; 86 using (SqlConnection con=new SqlConnection(constr)) 87 { 88 string sql = "Update UserLogin set ErrorTimes=0 where LoginId=@uid and ErrorTimes>=3"; 89 using (SqlCommand cmd=new SqlCommand(sql,con)) 90 { 91 cmd.Parameters.AddWithValue("@uid",loginId); 92 con.Open(); 93 cmd.ExecuteNonQuery(); 94 } 95 } 96 } 97 /// <summary> 98 /// 校验当前用户是否被锁定 99 /// </summary> 100 /// <param name="loginId">把刚才的用户传进来</param> 101 /// <returns></returns> 102 private bool IsLocked(string loginId) 103 { 104 //什么时候被锁定呢?登录错误三次并且被禁止登录时间在1分钟内。同时想下sql语句怎么去写。 105 string constr = "Data Source=HY-PC;Initial CataLog=itcastcn;Integrated Security=True"; 106 using (SqlConnection con=new SqlConnection(constr)) 107 { 108 string sql = "select count(*) from UserLogin where ErrorTimes>=3 and datediff(minute,LastLogintime,getdate())<1 and LoginId=@uid"; 109 //另外,复习下using。 110 //释放之后再return。 111 using (SqlCommand cmd=new SqlCommand(sql,con)) 112 { 113 cmd.Parameters.AddWithValue("@uid",loginId); 114 con.Open(); 115 //小细节:这需不需要提前判断下是不是空值?答:不用count(*)是个聚合函数。聚合函数查不到返回的也是0. 116 int r = Convert.ToInt32(cmd.ExecuteScalar()); 117 return r>0 ?true:false; 118 } 119 } 120 } 121 /// <summary> 122 /// 统计当前用户的错误信息 123 /// </summary> 124 /// <param name="loginId"></param> 125 private void WriteLoginLog(string loginId) 126 { 127 string constr = "Data Source=HY-PC;Initial Catalog=itcastcn;Integrated Security=True"; 128 using (SqlConnection con = new SqlConnection(constr)) 129 { 130 //确定sql语句 131 string sql = "update UserLogin set ErrorTimes=ErrorTimes+1,LastLoginTime=getdate() where loginId=@uid"; 132 //创建sqlcommand对象 133 using (SqlCommand cmd = new SqlCommand(sql, con)) 134 { 135 cmd.Parameters.AddWithValue("@uid", loginId); 136 con.Open(); 137 cmd.ExecuteNonQuery(); 138 } 139 } 140 } 141 } 142 }
接下来我们完成这两个需求:
数据导入:从文本文件导入用户信息。
数据导出:将用户信息导出到文本文件。
先看下通过设计器导出数据库中的一张表
十二、将数据库中的表导出一
十三、将数据库中的表导出二
十四、将数据库中的表导出三
十五、将数据库中的表导出四
十六、将数据库中的表导出五
十七、将数据库中的表导出六
十八、将数据库中的表导出七
十九、将数据库中的表导出八
二十、将数据库中的表导出九
二十一、导出的txt文件
接下来我们完成这两个需求:
数据导出:将用户信息导出到文本文件。
二十二、画好需要的界面
二十三、在输出窗口进行调试
二十四、数据导出的代码
数据导出的代码插入位置:
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.IO; 11 12 namespace _02数据导入导出到文本文件 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void button1_Click(object sender, EventArgs e) 22 { 23 //1.读取文件 24 string constr = "Data Source=HY-PC;Initial Catalog=itcastcn;Integrated Security=True"; 25 using (SqlConnection con=new SqlConnection(constr)) 26 { 27 //导出所有数据 28 string sql = "select*from TblClass"; 29 using (SqlCommand cmd=new SqlCommand(sql,con)) 30 { 31 //执行 32 con.Open(); 33 using (SqlDataReader reader=cmd.ExecuteReader()) 34 { 35 //判断是否查询出数据 36 if (reader.HasRows) 37 { 38 //2.将数据导出 39 using (StreamWriter sw=new StreamWriter(@"tblClass.txt")) 40 { 41 #region 第一行是列名 42 //4.读取出来列名 43 //获取列的个数 44 //reader.FieldCount 45 //根据索引获取列的名称 46 //reader.GetName(); 47 //有了上面这两个提示,我们循环就行了。 48 StringBuilder sbColumns = new StringBuilder(); 49 for (int i = 0; i < reader.FieldCount; i++) 50 { 51 sbColumns.Append(reader.GetName(i)+","); 52 } 53 //把最后多的逗号去掉 54 sbColumns.Remove(sbColumns.Length-1,1); 55 //再写到文件里面 56 sw.WriteLine(sbColumns.ToString()); 57 #endregion 58 while (reader.Read()) 59 { 60 //取每列 61 int id = reader.GetInt32(0); 62 string clsName = reader.GetString(1); 63 string clsDesc = reader.GetString(2); 64 //调试下,看能不能输出 65 //Console.WriteLine("{0},{1},{2}",id,clsName,clsDesc); 66 //3.每循环一次,写入一行 67 sw.WriteLine(string.Format("{0},{1},{2}", id, clsName, clsDesc)); 68 } 69 } 70 //最后提示一下用户 71 MessageBox.Show("导出成功!"); 72 } 73 else 74 { 75 MessageBox.Show("没有导出任何数据!"); 76 } 77 } 78 } 79 } 80 } 81 } 82 }
我们发现刚才我们导出的文件没有空值,那么遇到空值的时候怎么办呢?大家想下怎么处理?
好,现在思考下怎么导入呢?先读取文本文件,然后导入就行了。
读取的细节:不用读取列名和自动编号. 用split按照逗号,之后进行插入.
数据导入:从文本文件导入用户信息。
二十五、读取文本文件测试
二十六、为什么会在插入完一条数据之后报错
二十七、从文本文件导入用户信息的代码
从文本文件导入用户信息的代码插入位置:
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.IO; 11 12 namespace _02数据导入导出到文本文件 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 /// <summary> 21 /// 将数据导出 22 /// </summary> 23 /// <param name="sender"></param> 24 /// <param name="e"></param> 25 private void button1_Click(object sender, EventArgs e) 26 { 27 //1.读取文件 28 string constr = "Data Source=HY-PC;Initial Catalog=itcastcn;Integrated Security=True"; 29 using (SqlConnection con=new SqlConnection(constr)) 30 { 31 //导出所有数据 32 string sql = "select*from TblClass"; 33 using (SqlCommand cmd=new SqlCommand(sql,con)) 34 { 35 //执行 36 con.Open(); 37 using (SqlDataReader reader=cmd.ExecuteReader()) 38 { 39 //判断是否查询出数据 40 if (reader.HasRows) 41 { 42 //2.将数据导出 43 using (StreamWriter sw=new StreamWriter(@"tblClass.txt")) 44 { 45 #region 第一行是列名 46 //4.读取出来列名 47 //获取列的个数 48 //reader.FieldCount 49 //根据索引获取列的名称 50 //reader.GetName(); 51 //有了上面这两个提示,我们循环就行了。 52 StringBuilder sbColumns = new StringBuilder(); 53 for (int i = 0; i < reader.FieldCount; i++) 54 { 55 sbColumns.Append(reader.GetName(i)+","); 56 } 57 //把最后多的逗号去掉 58 sbColumns.Remove(sbColumns.Length-1,1); 59 //再写到文件里面 60 sw.WriteLine(sbColumns.ToString()); 61 #endregion 62 while (reader.Read()) 63 { 64 //取每列 65 int id = reader.GetInt32(0); 66 string clsName = reader.GetString(1); 67 string clsDesc = reader.GetString(2); 68 //调试下,看能不能输出 69 //Console.WriteLine("{0},{1},{2}",id,clsName,clsDesc); 70 //3.每循环一次,写入一行 71 sw.WriteLine(string.Format("{0},{1},{2}", id, clsName, clsDesc)); 72 } 73 } 74 //最后提示一下用户 75 MessageBox.Show("导出成功!"); 76 } 77 else 78 { 79 MessageBox.Show("没有导出任何数据!"); 80 } 81 } 82 } 83 } 84 } 85 /// <summary> 86 /// 将数据导入 87 /// </summary> 88 /// <param name="sender"></param> 89 /// <param name="e"></param> 90 private void button2_Click(object sender, EventArgs e) 91 { 92 //1.读取文本文件中的数据 93 using (StreamReader sr=new StreamReader("TblClass.txt")) 94 { 95 //跳过第一行 96 sr.ReadLine(); 97 //2.插入数据库 98 string constr = "Data Source=HY-PC;Initial Catalog=itcastcn;Integrated Security=True"; 99 //连接对象 100 using (SqlConnection con=new SqlConnection(constr)) 101 { 102 string sql = "insert into TblClass(tClassName,tClassDesc) values(@clsName,@clsDesc)"; 103 using (SqlCommand cmd=new SqlCommand(sql,con)) 104 { 105 //3.先加好参数 106 cmd.Parameters.Add(new SqlParameter("@clsName",SqlDbType.VarChar)); 107 cmd.Parameters.Add(new SqlParameter("@clsDesc", SqlDbType.VarChar)); 108 //打开,放到最外面 109 con.Open(); 110 //考虑编码问题,刚才我们导出的时候是utf-8现在也是utf-8,不会乱码。 111 //读取每一行 112 while (!sr.EndOfStream) 113 { 114 string line = sr.ReadLine(); 115 //判断分割一下 116 if (!string.IsNullOrEmpty(line)) 117 { 118 string[] columnData = line.Split(','); 119 //添加参数,每次添加的参数不一样 120 //报错的原因是,当第二次插入的时候,参数重名了。两种解决办法,一种是传完个参数清空下参数。第二种是在循环外面加好参数。 121 //cmd.Parameters.AddWithValue("@clsName",columnData[1]); 122 //cmd.Parameters.AddWithValue("@clsDesc",columnData[2]); 123 124 //在里面修改每个参数的值就行了。 125 cmd.Parameters["@clsName"].Value = columnData[1]; 126 cmd.Parameters["@clsDesc"].Value = columnData[2]; 127 //执行 128 cmd.ExecuteNonQuery(); 129 #region 读取文本文件测试 130 //打印一下,看能不能输出来 131 //Console.WriteLine("{0}-{1}-{2}",columnData[0],columnData[1],columnData[2]); 132 #endregion 133 //cmd.Parameters.Clear(); 134 } 135 } 136 MessageBox.Show("导入成功!"); 137 } 138 } 139 } 140 } 141 } 142 }
接下来我们做下省市联动功能:
二十八、画好省市界面
要加载省,我们发现有可能在A数据库里面,也有也能在B数据库里面。数据库连接字符串总在变,以前我们是写死在程序里面不好,我们能不能把连接字符串放在配置文件里面?接下来我们就用配置文件来做。
二十九、添加配置文件
三十、写好配置文件
三十一、连接字符串添加引用
三十二、怎么把id也加进来
写个类可以解决这个问题,把这两个参数赋给这个属性,然后把这个对象加进来。下面我们照着这个思路写一下。
三十三、将两个值封装成一个类
三十四、解决类里面出现的问题
思考:在界面上单击按钮,把id打印出来。
三十五、查询数据加载省数据代码
省市联动查询数据加载数据代码插入位置:
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.Configuration; 10 using System.Data.SqlClient; 11 12 namespace _03省市联动 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void Form1_Load(object sender, EventArgs e) 22 { 23 //1.窗体加载的时候将省份信息加载到第一个combobox中。 24 //写个方法 25 LoadProvinceData(); 26 } 27 28 private void LoadProvinceData() 29 { 30 //创建连接字符串 31 string constr=ConfigurationManager.ConnectionStrings["sql"].ConnectionString; 32 using (SqlConnection con=new SqlConnection(constr)) 33 { 34 //建议一般写sql语句的时候,在select语句中,用到哪列写哪列,不要动不动 就写*。 35 string sql = "select AreaId,AreaName from TblArea where AreaPid=0"; 36 using (SqlCommand cmd=new SqlCommand(sql,con)) 37 { 38 con.Open(); 39 using (SqlDataReader reader = cmd.ExecuteReader()) 40 { 41 if (reader.HasRows) 42 { 43 while (reader.Read()) 44 { 45 //这的第0列,取决于我们查询的顺序。 46 int id = reader.GetInt32(0); 47 string name = reader.GetString(1); 48 //查询出来之后往里面加 49 //把两个值封装成一个类,然后传进去。 50 Area area = new Area(id, name); 51 cboProvince.Items.Add(area); 52 } 53 } 54 } 55 } 56 } 57 } 58 } 59 }
三十六、小细节
接下来写代码点按钮获取选中项
三十七、获取当前选中项的id和省名称
三十八、获取当前选中项的id和省名称的代码
下面我们做当选中省份的时候,对应显示出城市的列表。想下,在什么事件里面做这个东西呢?
三十九、属性改变时发生的事件
四十、清空加载项
四十一、根据父id查询城市并加载代码
省市联动演示
配置文件每次需要跟着拷走么?是的,没有配置文件,会提示找不到配置文件。
四十二、修改配置文件
如果换成了web是web.config文件,咱们这是exe.config文件。
接下来我们看这么一个案例:
四十三、资料管理器升级案例
先看下数据库中的两个表:
四十四、两张表
表一是主键表,表二是外键表。
四十五、资料管理器的界面
第一步:当窗体加载的时候,把文章列别加载到左边的treeview中。
四十六、加载类别-1下的节点代码
四十七、加载类别下的子节点的代码
好,上面的代码对于初学者来讲可能比较的难理解,下面我把上面做的功能单拿出来再做一遍。
四十八、存储数据的类
四十九、递归加载子节点
下面我们做下,选中某个类别把文章加载到右上方。下面显示文章的内容。
首先给节点注册一个鼠标双击事件:
先找到双击事件,再去判断是软件开发那个节点还是C#、Java、Php节点?
五十、节点的鼠标双击事件
五十一、存数文章信息的类
五十二、双击子节点将文章显示到listbox的代码
最后我们做下显示标题对应的文章
五十三、找到相应的事件,一般默认的是最常用的
五十四、显示标题对应的文章的代码
五十五、更新数据内容的sql语句
五十六、资料管理器部分功能演示
作者近期文章列表:
C#基础教程(完全免费,献给代码爱好者的最好礼物。注:本作者分享自己精心整理的C#基础教程,无任何商业目的。 希望与更多的代码爱好者交流心得,也请高手多多指点!!!) |
|
三层 | 三层(一) |
三层相关案例(及常见的错误) | |
三层实例(内涵Sql CRUD) | |
SQL数据库 ADO.net | 数据库的应用图解一 |
数据库的应用详解二 | |
ADO.NET(内涵效率问题) | |
面向过程,面向对象中高级 | 面向过程,面向对象的深入理解一 |
面向过程,面向对象的深入理解二 | |
面向对象的深入理解三 | |
winform基础 | Winform基础 |
winform中常用的控件 | |
面向过程 | 三种循环的比较 |
C#中的方法(上) | |
我们常见的数组 | |
面向对象 | 思想的转变 |
C#中超级好用的类 | |
C#中析构函数和命名空间的妙用 | |
C#中超级好用的字符串 | |
C#中如何快速处理字符串 | |
值类型和引用类型及其它 | |
ArrayList和HashTable妙用一 | |
ArrayList和HashTable妙用二 | |
文件管理File类 | |
多态 | |
C#中其它一些问题的小节 | |
GDI+ | 这些年我收集的GDI+代码 |
这些年我收集的GDI+代码2 | |
HTML概述以及CSS | 你不能忽视的HTML语言 |
你不能忽视的HTML语言2精编篇 | |
你不能忽视的HTML语言3 | |
CSS基本相关内容--中秋特别奉献 | |
CSS基本相关内容2 | |
JavaScript基础 | JavaScript基础一 |
jQuery | jQuery(内涵: jquery选择器) |