c#基础之DataSet与DataTable
(1)DataSet是数据集,DataTable是数据表,DataSet存储多个DataTable。DataSet和DataTable像是专门存储数据的一个容器,在你查询数据库得到一些结果时可以存在里面。DataReader数据处理速度快,但它是只读的,一旦移到下一行就不能查看上一行的数据,DataSet则可以自由移动指针。DataSet的数据是与数据库断开的。DataSet还可用于多层应用程序中,如果应用程序运行在中间层的业务对象中来访问数据库,则业务对象需将脱机数据结构传递给客户应用程序。
(2)创建函数
DataTable() 不带参数初始化DataTable 类的新实例。
DataTable(string tableName) 用指定的表名初始化DataTable 类的新实例。
DataTable(string tableName, string tableNamespace) 用指定的表名和命名空间初始化DataTable 类的新实例。
(3)SqlDataAdapter是 DataSet和 SQL Server之间的桥接器,用于检索和保存数据。SqlDataAdapter通过对数据源使用适当的Transact-SQL语句映射 Fill(它可更改DataSet中的数据以匹配数据源中的数据)和 Update(它可更改数据源中的数据以匹配 DataSet中的数据)来提供这一桥接。当SqlDataAdapter填充 DataSet时,它为返回的数据创建必需的表和列(如果这些表和列尚不存在)。
如果只需要执行SQL语句或SP,就没必要用到DataAdapter ,直接用SqlCommand的Execute系列方法就可以了。sqlDataadapter的作用是实现Dataset和DB之间的桥梁:比如将对DataSet的修改更新到数据库。
由于DataSet是离线处理,所以当在事务处理中要锁定数据库时,不可以使用DataSet。因为当DataSet被填充以后,会自动断开与数据库的连接,此时不可能再对数据库进行锁定。
SqlDataAdapter的UpdateCommand的执行机制是:当调用SqlDataAdapter.Update()时,检查DataSet中的所有行,然后对每一个修改过的Row执行SqlDataAdapter.UpdateCommand ,也就是说如果未修改DataSet中的数据,SqlDataAdapter.UpdateCommand不会执行。
(4)
//(1)构造连接字符串:如果integrated security=true表示可以在不知道数据库用户名和密码的情况下时,依然可以连接数据库,如果integrated security=false,或者不写,表示一定要输入正确的数据库登录名和密码。sspi ,相当于 True,建议用这个代替 True。 string connSQL = @"data source= ;initial catalog= ;persist security info=True;user id= ;password= ;MultipleActiveResultSets=True"; //(3)打开到数据库的连接 :SqlConnection表示一个到 SQL Server 数据库的打开的连接 using (SqlConnection conn = new SqlConnection(connSQL)) { //(4)打开连接 conn.Open(); //(5)获取数据库表的数据 string strSql = "SELECT * FROM 表名"; SqlCommand cmd = new SqlCommand(strSql, conn); //(6)示用于填充 System.Data.DataSet 和更新 SQL Server 数据库的一组数据命令和一个数据库连接。 此类不能被继承。 SqlDataAdapter da = new SqlDataAdapter(cmd); //(7)创建dataset示例 DataSet ds = new DataSet(); //填充 da.Fill(ds, "表名"); //获取dataset中的多表多行多列。 foreach (DataTable d in ds.Tables) { foreach (DataRow dr in d.Rows) ///遍历所有的行 foreach (DataColumn dc in d.Columns) //遍历所有的列 Console.WriteLine("{0}, {1}, {2}", d.TableName, dc.ColumnName, dr[dc]); //表名,列名,单元格数据 // Console.WriteLine(d.TableName); } Console.ReadKey(); conn.Close(); }
也可以将数据直接放到DataTable中
// 在使用Fill方式时,可以指定DataTable,而不是DataSet: DataTable tbl = new DataTable("sys_menu"); da.Fill(tbl); foreach (DataRow row in tbl.Rows) foreach (DataColumn col in tbl.Columns) Console.WriteLine("表名:{0},{1},{2} ", tbl.TableName, col,row[col]); Console.ReadKey();
(5)
1、在C#中,如果要删除DataTable中的某一行,大约有以下几种办法:
•使用DataTable.Rows.Remove(DataRow),或者DataTable.Rows.RemoveAt(index);可以直接删除行
•datatable.Rows[i].Delete()。Delete()之后需要datatable.AccepteChanges()方法确认完全删除,因为Delete()只是将相应列的状态标志为删除,还可以通过datatable.RejectChanges()回滚,使该行取消删除。
•在删除DataTable中的行的时候,每删除一行,DataTable中所有行的索引都会发生改变。在循环删除DataTable.Row的时候不能使用foreach。使用foreach进行循环的时候,是不允许Table有删除和添加操作的。
•如果是按某列为条件进行删除,则每删完一行,整个Table的index就会立即发生变化,等于Table已经变成了一个新的表。但是索引却已经加1了。于是会造成第一列永远匹配不到。因此,每删除完一行,要跟着判断第一行是否满足删除条件。
•如果要删除DataTable中的多行,应该采用倒序循环DataTable.Rows。因为正序删除时索引会发生变化。程式发生异常,很难预料后果。(不要在循环里使用myDataTable.Rows.RemoveAt(i).因为每删除一行后.i的值会增加,但行数会是减少了.这么做一定会出错.因此要遍历数据,使用Remove方式时,要倒序的遍历)
for (int i = count -1; i >=0; i--)
{
ds.Tables[0].Rows.RemoveAt(i);
}
总结:
delete和remove
•Delete的使用是 datatable.Rows[i].Delete();
•Remove的使用是datatable.Rows.Remove(datatable.Rows[i]);
•这两个的区别是,使用delete后,只是该行被标记为deleted,但是还存在,用Rows.Count来获取行数时,还是删除之前的行数.需要使用datatable.AcceptChanges()方法来提交修改.
而Remove方法则是直接删除.(有时候发现使用ds.Tables[0].Rows[i].Delete();数据行也被直接删除了,原因是在创建datatable时没有执行AcceptChanges这个方法,那么在delete时,不会标记,会直接删除掉。)
•如果在for循环里删除行的话,最好是使用delete方法,不然会出现count变化的情况.循环完后再使用AcceptChanges()方法提交修改,删除掉标记为deleted的行
---注意:(5)是引入的,但是不知道真正的源头来自哪里。
DataTable dt = new DataTable("ta"); DataRow dr; dt.Columns.Add(new DataColumn("主键 ", typeof(Int32))); dt.Columns.Add(new DataColumn("字符串值 ", typeof(string))); dt.Columns.Add(new DataColumn("日期时间值 ", typeof(DateTime))); dt.Columns.Add(new DataColumn("布尔值 ", typeof(bool))); for (int i = 1; i <= 2; i++) { dr = dt.NewRow(); dr[0] = i; dr[1] = "项 " + i.ToString(); dr[2] = DateTime.Now; dr[3] = (i % 2 != 0) ? true : false; dt.Rows.Add(dr); } foreach (DataRow row in dt.Rows) foreach (DataColumn col in dt.Columns) Console.WriteLine("{0},{1},{2}",dt.TableName,col.ColumnName,row[col] ); dt.Rows[1].Delete(); foreach (DataRow row in dt.Rows) foreach (DataColumn col in dt.Columns) Console.WriteLine("AcceptChanges之前 {0},{1},{2}", dt.TableName, col.ColumnName, row[col]); // 提交自上次调用 System.Data.DataTable.AcceptChanges() 以来对该表进行的所有更改。 dt.AcceptChanges();
排序:
DataTable dt = new DataTable("ta"); DataRow dr; dt.Columns.Add(new DataColumn("ID", typeof(Int32))); for (int i = 1; i <= 6; i++) { dr = dt.NewRow(); dr[0] = i; dt.Rows.Add(dr); } foreach (DataRow row in dt.Rows) foreach (DataColumn col in dt.Columns) Console.WriteLine("{0},{1},{2}",dt.TableName,col.ColumnName,row[col] ); // 表示用于排序、筛选、搜索、编辑和导航的 System.Data.DataTable 的可绑定数据的自定义视图。 DataView dv = dt.DefaultView; dv.Sort = "ID DESC"; dt = dv.ToTable(); foreach (DataRow row in dt.Rows) foreach (DataColumn col in dt.Columns) Console.WriteLine("排序后的: {0},{1},{2}", dt.TableName, col.ColumnName, row[col]); Console.ReadKey();
ta,ID,1
ta,ID,2
ta,ID,3
ta,ID,4
ta,ID,5
ta,ID,6
排序后的: ta,ID,6
排序后的: ta,ID,5
排序后的: ta,ID,4
排序后的: ta,ID,3
排序后的: ta,ID,2
排序后的: ta,ID,1
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术