(转 飛雲若雪)ADO.NET
1 一、简单介绍ADO.NET 2 3 System.Data:DataTable,DataSet,DataRow,DataColumn,DataRelation,Constraint 4 5 System.Data.Common(各种数据访问类的基类和接口):DataColumnMapping,DataTableMapping 6 7 System.Data.SqlClient(对Sql Server进行操作的数据访问类): 8 9 1)SqlConnection:数据库连接器 10 11 2)SqlCommand:数据库命名对象 12 13 3)SqlCommandBuilder:生存SQL命令 14 15 4)SqlDataReader:数据读取器 16 17 5)SqlDataAdapter:数据适配器,填充DataSet 18 19 6)SqlParameter:为存储过程定义参数 20 21 7)SqlTransaction:数据库事务 22 23 二、SqlConnection(连接对象) 24 25 1、连接字符串 26 27 基本语法:数据源(Data Source)+数据库名称(Initial Catalog)+用户名(User ID)+密码(Password)。 28 29 说明: 30 (1)必须指定Sql Server支持的两种身份验证方法(即Windows身份验证和Sql Server身份验证)中的一种。要想使用Windows身份验证,必须在连接字符串中包括 Integrated Security 属性:Data Source=ServerName;Integrated Security=True;默认情况下,Integrated Security属性为False。 31 32 (2)连接字符串中可用的选项: 33 34 Application Name(应用程序名称):应用程序的名称。如果没有被指定的话,它的值为.NET SqlClient Data Provider(数据提供程序)。 35 36 AttachDBFileName/extended properties(扩展属性)/Initial File Name(初始文件名): 可连接数据库的主要文件的名称,包括完整路径名称。数据库名称必须用关键字数据库指定。 37 38 Connect Timeout(连接超时)/Connection Timeout(连接超时):一个到服务器的连接在终止之前等待的时间长度(以秒计),缺省值为15。 39 40 Connection Lifetime(连接生存时间): 当一个连接被返回到连接池时,它的创建时间会与当前时间进行对比。如果这个时间跨度超过了连接的有效期的话,连接就被取消。其缺省值为0。 41 42 Connection Reset(连接重置): 表示一个连接在从连接池中被移除时是否被重置。缺少值为真。 43 44 Current Language(当前语言): SQL Server语言记录的名称。 45 46 Data Source(数据源)/Server(服务器)/Address(地址)/Addr(地址)/Network Address(网络地址):SQL Server实例的名称或网络地址。 47 48 Encrypt(加密):当值为真时,如果服务器安装了授权证书,SQL Server就会对所有在客户和服务器之间传输的数据使用SSL加密。被接受的值有true(真)、false(伪)、yes(是)和no(否)。 49 50 Enlist(登记):表示连接池程序是否会自动登记创建线程的当前事务语境中的连接,其缺省值为真。 51 52 Database(数据库)/Initial Catalog(初始编目):数据库的名称。 53 54 Integrated Security(集成安全)/Trusted Connection(受信连接):表示Windows认证是否被用来连接数据库。它可以被设置成真、伪或者是和真对等的sspi,其缺省值为伪。 55 56 Max Pool Size(连接池的最大容量): 连接池允许的连接数的最大值,其缺省值为100。 57 58 Min Pool Size(连接池的最小容量): 连接池允许的连接数的最小值,其缺省值为0。 59 60 Network Library(网络库)/Net(网络):用来建立到一个SQL Server实例的连接的网络库。支持的值包括: dbnmpntw (Named Pipes)、dbmsrpcn (Multiprotocol/RPC)、dbmsvinn(Banyan Vines)、dbmsspxn (IPX/SPX)和dbmssocn (TCP/IP)。协议的动态链接库必须被安装到适当的连接,其缺省值为TCP/IP。 61 62 Packet Size(数据包大小):用来和数据库通信的网络数据包的大小。其缺省值为8192。 63 64 Password(密码)/Pwd:与帐户名相对应的密码。 65 66 Persist Security Info(保持安全信息):用来确定一旦连接建立了以后安全信息是否可用。如果值为真的话,说明像用户名和密码这样对安全性比较敏感的数据可用,而如果值为伪则不可用。重置连接字符串将重新配置包括密码在内的所有连接字符串的值。其缺省值为伪。 67 68 Pooling(池):确定是否使用连接池。如果值为真的话,连接就要从适当的连接池中获得,或者,如果需要的话,连接将被创建,然后被加入合适的连接池中。其缺省值为真。 69 70 User ID(用户ID):用来登陆数据库的帐户名。 71 72 Workstation ID(工作站ID):连接到SQL Server的工作站的名称。其缺省值为本地计算机的名称。 73 74 (3)例举典型连接字符串 75 76 1>SQL Server连接字符串 77 78 标准安全连接: 79 80 Data Source=myServerAddress;Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;或者 81 82 Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;Trusted_Connection=False; 83 84 可信连接: 85 86 Data Source=myServerAddress;Initial Catalog=myDataBase;Integrated Security=SSPI;或者 87 88 Server=myServerAddress;Database=myDatabase;Trusted_Connection=True; 89 90 2> Access连接字符串 91 92 Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\myDatabase.mdb;User Id=admin;Password=; 93 94 3>MySQL连接字符串 95 96 Server=myServerAddress;Database=myDatabase;Uid=myUsername;Pwd=myPassword; 97 98 4>DB2连接字符串 99 100 Server=myAddress:myPortNumber;Database=myDatabase;UID=myUsername;PWD=myPassword; 101 102 5>Oracle连接字符串 103 104 Data Source=TORCL;User Id=myUsername;Password=myPassword; 105 106 2、创建连接对象 107 108 SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder() 109 { 110 DataSource = "", 111 InitialCatalog = "", 112 UserID = "", 113 Password = "" 114 }; 115 SqlConnection connection = new SqlConnection(connectionStringBuilder.ToString()); 116 117 3、打开和关闭连接对象 118 119 using(SqlConnection connection = new SqlConnection(connectionStringBuilder.ToString())) 120 { 121 connection.Open(); 122 connection.Close(); 123 } 124 4、连接池 125 126 (1)建立一个数据库连接是非常消耗时间和消耗资源的事情。ADO.NET提供了名为连接池的优化方法。连接池就是一个容器,它存放了一定数量的与数据库服务器的物理连接。因此,当我们需要连接数据库服务器的时候,只需去池(容器)中取出一条空闲的连接,而不是新建一条连接。这样的话,我们就可以大大减少连接数据库的开销,从而提高了应用程序的性能。 127 128 (2)连接池具有类别区分,同一个时刻同一应用程序域可以有多个不同类型的连接池。细致的讲,连接池是由进程、应用程序域、连接字符串以及windows标识(在使用集成的安全性时)共同组成签名来标识区分的。但对于同一应用程序域来说,一般只由连接字符串来标识区分。当打开一条连接时,如果该条连接的类型签名与现有的连接池类型不匹配,则创建一个新的连接池。反之,则不创建新的连接池。 129 130 例如: 131 132 //创建连接对象1 133 using (SqlConnection conn1 = new SqlConnection("DataSource=(local);Integrated Security=SSPI;Initial Catalog=Northwind")) 134 { 135 conn1.Open(); 136 } 137 138 139 //创建连接对象2 140 using (SqlConnection conn2 = new SqlConnection("DataSource=(local);Integrated Security=SSPI;Initial Catalog=pubs")) 141 { 142 conn2.Open(); 143 } 144 145 146 //创建连接对象3 147 using (SqlConnection conn3 = new SqlConnection("DataSource=(local);Integrated Security=SSPI;Initial Catalog=Northwind")) 148 { 149 conn3.Open(); 150 } 151 152 创建了三个SqlConnection对象,但是管理时只需要两个连接池。因为conn1与conn2的连接字符串相同,所有可以共享一个连接池。conn2与conn1,conn3不同,所以需要创建新的连接池。 153 (3)连接池行为可以通过连接字符串控制: 154 155 Connection Timeout:连接请求等待超时时间。默认为15秒。 156 157 Max Pool Size:连接池中最大连接数。默认为100。 158 159 Min Pool Size:连接池中最小连接数。默认为0。 160 161 Pooling:是否启用连接池。ADO.NET默认是启动的。 162 163 (4)使用T-SQL语句监视连接状态:exec sp_who 164 165 (5)高效使用连接池的基本原则: 166 167 ● 在最晚的时刻申请连接,在最早的时刻释放连接。 168 169 ● 关闭连接时先关闭相关用户定义的事务。 170 171 ● 确保并维持连接池中至少有一个打开的连接。 172 173 ● 尽力避免池碎片的产生。主要包括集成安全性产生的池碎片以及使用许多数据库产生的池碎片。 174 三、SqlCommand(命令对象) 175 176 1、实例化的时候默认初始化的四个属性: 177 178 CommandText:空字符串("") 179 180 CommandTimeout:30 181 182 CommandType:CommandType.Text 183 184 Connection:Null 185 186 2、创建命令对象: 187 188 SqlCommand command = connection.CreateCommand(); //这种方式比较好 189 SqlCommand command=new SqlCommand(); 190 191 3、几个重要的属性: 192 193 (1)CommandText:获取或设置要对数据源执行的 Transact-SQL 语句、表名或存储过程! 194 195 (2)CommandType:设置你执行的SQL语句是存储过程还是T-SQL(是一个枚举)! 196 197 ● Text:SQL文本命令(默认) 198 199 ● StoredProcedure:存储过程名称 200 ● TableDirect:表的名称 201 202 (3)Parameters:设置你T-SQL中你需要用到的参数。 203 204 4、几个重要的方法: 205 206 (1)ExecuteNonQuery:返回是影响的行数(int),主要执行更新,添加,删除等操作! 207 208 (2)ExecuteReader:执行SQL或存储过程,返回的是SqlDataReader类型,主要用来查询! 209 210 注意这个方法的重载CommandBehavior枚举,成员如下: 211 212 ● Default:此查询可能返回多个结果集。执行查询可能会影响数据库状态。当不设置CommandBehavior标志时默认为Default。 213 214 ● SingleResult:查询返回个结果集。 215 216 ● SchemaOnly:查询仅返回列信息。当使用SchemaOnly时,用于SQL Server的.NET Framework数据提供程序将在要执行的语句前加上SET FMTONLY ON。 217 218 ● KeyInfo:此查询返回列和主键信息。 219 220 ● SingleRow: 查询应返回一行。 221 222 ● SequentialAccess:提供一种方法,以便DataReader处理包含带有大量二进制值的列的行。SequentialAccess不是加载整行,而是使DataReader将数据作为流来加载。然后可以使用GetBytes或GetChars方法来指定开始读取操作的字节位置以及正在返回的数据的有限的缓冲区大小。 223 224 ● CloseConnection:在执行该命令时,如果关闭关联的DataReader对象,则关联的Connection对象也将关闭。 225 226 (3)ExecuteScalar:返回执行结果集中的第一行第一列,如果没有数据,则返回NULL! 227 228 可能返回NULL值,需要对结果进行判断,如下: 229 230 object my = cmd.ExecuteScalar(); 231 if (object.Equals(my,null)) //可以使用Equals进行Null值的判断,易读性强 232 Console.WriteLine("Not Data"); 233 else 234 Console.WriteLine("Yes"); 235 (4)CreateParameter:创建SqlParameter实例。 236 237 5、异步执行命令:执行Command对象命令时,需要等待命令完成才能执行其他操作。比如,执行ExcuteNonQuery()方法,应用程序将会保持阻塞,直到数据操作成功完成或者异常终止以及连接超时。异步执行的根本思想是,在执行命令操作时,无需等待命令操作完成,可以并发的处理其他操作。ADO.NET提供了丰富的方法来处理异步操作,BeginExecuteNonQuery和EndExcuteNonQuery就是一对典型的为异步操作服务的方法。BeginExecuteNonQuery方法返回System.IAsyncResult接口对象。我们可以根据IAsyncResult的IsCompleted属性来轮询(检测)命令是否执行完成。 238 239 using System; 240 using System.Collections.Generic; 241 using System.Linq; 242 using System.Text; 243 using System.Data;//必须引入 244 using System.Data.SqlClient;//必须引入 245 namespace Command 246 { 247 class Program 248 { 249 static void Main(string[] args) 250 { 251 SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder(); 252 connStr.DataSource = @".\SQLEXPRESS"; 253 connStr.IntegratedSecurity = true; 254 connStr.InitialCatalog = "db_MyDemo"; 255 connStr.AsynchronousProcessing = true;//必须显示说明异步操作 256 StringBuilder strSQL = new StringBuilder(); 257 //插入100个测试客户 258 for (int i = 1; i <= 500; ++i) 259 { 260 strSQL.Append("insert into tb_SelCustomer "); 261 strSQL.Append("values('"); 262 string name = "测试客户" + i.ToString(); 263 strSQL.Append(name); 264 strSQL.Append("','0','0','13822223333','liuhaorain@163.com','广东省深圳市宝安区',12.234556,34.222234,'422900','备注信息'); "); 265 } 266 using (SqlConnection conn = new SqlConnection(connStr.ConnectionString)) 267 { 268 conn.Open(); 269 SqlCommand cmd = new SqlCommand(strSQL.ToString(), conn); 270 IAsyncResult pending = cmd.BeginExecuteNonQuery();//开始执行异步操作 271 double time = 0; 272 //检查异步处理状态 273 while (pending.IsCompleted == false) 274 { 275 System.Threading.Thread.Sleep(1); 276 time++; 277 Console.WriteLine("{0}s", time * 0.001); 278 } 279 if (pending.IsCompleted == true) 280 { 281 Console.WriteLine("Data is inserted completely...\nTotal coast {0}s", time * 0.001); 282 } 283 cmd.EndExecuteNonQuery(pending);//结束异步操作 284 } 285 Console.Read(); 286 } 287 } 288 } 289 290 6、如何获取插入行的ID 291 292 using System; 293 using System.Collections.Generic; 294 using System.Linq; 295 using System.Text; 296 using System.Data; 297 using System.Data.SqlClient; 298 namespace Command2 299 { 300 class Program 301 { 302 static void Main(string[] args) 303 { 304 //构造连接字符串 305 SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder(); 306 connStr.DataSource = @".\SQLEXPRESS"; 307 connStr.IntegratedSecurity = true; 308 connStr.InitialCatalog = "db_MyDemo"; 309 //拼接SQL语句 310 StringBuilder strSQL = new StringBuilder(); 311 strSQL.Append("insert tb_SelCustomer(Name) "); 312 strSQL.Append("OUTPUT inserted.ID values(@Name)"); 313 using (SqlConnection conn = new SqlConnection(connStr.ConnectionString)) 314 { 315 SqlCommand cmd = new SqlCommand(strSQL.ToString(), conn); 316 SqlParameter para = new SqlParameter("@Name", SqlDbType.VarChar, 20); 317 para.Value = "Kemi"; 318 cmd.Parameters.Add(para); 319 try 320 { 321 conn.Open(); 322 int insertedID = (int)cmd.ExecuteScalar();//获取单个值 323 Console.WriteLine("Inserted ID:{0}", insertedID); 324 } 325 catch (Exception ex) 326 { 327 Console.WriteLine("{0}", ex.Message); 328 } 329 } 330 Console.Read(); 331 } 332 } 333 } 334 335 四、SqlParameter(Sql参数) 336 337 1、几个重要的属性 338 339 ParameterName:设置参数名 340 341 Value:给参数设置值 342 343 Size:设置参数字节最大大小 344 345 SqlDbType:参数在SQL中的类型 346 347 2、命令对象添加参数集合的几种方法 348 349 (1)AddWithValue 350 351 (2)Add 352 353 (3)AddRange 354 355 using (SqlConnection connection = new SqlConnection("")) 356 { 357 SqlCommand command = connection.CreateCommand(); 358 command.CommandText = ""; 359 360 //可以使用这种方式添加多个参数,不过方式不够好 361 command.Parameters.Add("@name", SqlDbType.NVarChar).Value = "yang"; //第一种方式 362 command.Parameters.Add("@age", SqlDbType.Int).Value = 888; 363 command.Parameters.Add("@address", SqlDbType.NVarChar, 100).Value = "Jiang Su"; 364 365 //这种方式直接给定参数名和参数就可以了,可操作性比较差 366 command.Parameters.AddWithValue("@name", "yang"); 367 command.Parameters.AddWithValue("@age", 888).SqlDbType = SqlDbType.Int; 368 command.Parameters.AddWithValue("@address", "Jiang su").SqlDbType = SqlDbType.NVarChar; 369 370 //直接使用参数集合添加你需要的参数,推荐这种写法 371 SqlParameter[] parameters = new SqlParameter[] 372 { 373 new SqlParameter("@name",SqlDbType.NVarChar,100){Value = "yang"}, 374 new SqlParameter("@age",SqlDbType.Int,2){Value = 888}, 375 new SqlParameter("@address",SqlDbType.NVarChar,20){Value = "Jiang Su"}, 376 }; 377 command.Parameters.AddRange(parameters); //参数也可以是一个Array数组,如果采用数组参数代码的可读性和扩展性就不是那么好了 378 379 //当我们把参数都添加好之后,会生成一个“SqlParameterCollection”集合类型,相当于参数的集合 380 //那么我们就可以对这些参数进行修改和移除了 381 //说穿了“SqlParameterCollection”内部其实是一个List<SqlParameter>的集合,只是它里面的复杂度比较高,考虑的很全面 382 command.Parameters[0].Value = "hot girl"; 383 command.Parameters[0].Size = 200; 384 } 385 386 五、SqlDataReader(数据流读取器) 387 388 1、基本用法 389 390 using (SqlConnection conn = new SqlConnection("")) 391 { 392 conn.Open(); 393 SqlCommand command = conn.CreateCommand(); 394 command.CommandText = ""; 395 using (SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection)) 396 { 397 while (dr.Read()) 398 { 399 //开始读取数据了,接下来你想怎么样就怎么样了 400 string str = dr.GetSqlString(0).ToString(); 401 } 402 } 403 } 404 405 2、常用方法 406 407 (1)GetOrdinal:获取指定列名的列序号(索引号),使用这个方法可以把经常变动的列进行固定 408 409 int name=dr.GetOrdinal("name"); //通过列名来获取当前列的索引号 410 411 (2)GetName: 获取列名,参数为指定列名的序列号,返回string 412 413 string columnName=dr.GetName(name);//通过列名所处的索引号来获取列名名称 414 415 (3)IsDBNull:判断当前读取的数据是否为Null,返回类型为Bool 416 417 dr.IsDBNull(coContractID)?"NULL":dr.GetInt32(coContractID).ToString(); 418 419 (4)NextResult:当查询为批处理查询时,使用这个方法去读取下一个结果集,返回值为Bool,如果存在多个结果集,则为 true;否则为 false。 420 421 (5)Read:读取数据 422 423 3、常用属性 424 425 (1)HasRow:判断是否包含一行或多行,也就是判断有没有数据,返回类型为Bool。 426 427 (2)FieldCount:获取读取的列数,返回类型为Int。 428 429 (3)IsClosed:判断读取的数据流是否关闭。 430 431 4、性能剖析 432 433 读取数据的时候会有很多种写法,如dr[0].ToString(),dr["Name"].ToString(),dr.GetString(0),dr.GetSqlString(0)等等的读取方式的写法。 434 435 读取数据性能总结: 436 437 SqlDataReader读取方法 438 439 (1)DataReader 索引+基于[序列号]->dr[0].ToString() |Index-based access 440 441 (2)DataReader 索引+基于[列名]->dr["Name"].ToString() |性能最差 442 443 (3)GetString 开头的+基于[序列号]->dr.GetString(0) |type-access 444 445 (4)GetSql 开头的+基于[序列号]->dr.GetSqlString(0) |Provider-specific typed accessor 446 447 (5)GetOrdinal() 通过列名获取这个列的序列号 |这个方法在提高性能上面有作用 448 449 性能(4)-->(3)-->(1)-->(2) 450 451 5、补充 452 453 SqlDataReader是连接相关的,SqlDataReader中的查询结果并不是放在程序中,而是放在数据库服务器中,SqlDataReader只是相当于一个指针(游标),只能读取当前游标指向的行,连接断开就不能再读取。这样无论查询结果有多少条,对程序占用的内存都几乎没有影响。但SqlDataReader对于小数据量的数据来说带来的只有麻烦。 454 455 六、SqlTransaction(事务) 456 457 1、事务中的命名存储点 458 459 一旦你定义了命名存储点,只能回滚命名存储点之后的操作,这是要看情况而使用! 460 461 using (SqlConnection conn = new SqlConnection(str)) 462 { 463 conn.Open(); 464 SqlTransaction transaction = conn.BeginTransaction(); 465 SqlCommand cmd = conn.CreateCommand(); 466 cmd.CommandText = ""; 467 cmd.Transaction = transaction; 468 //使用命名存储点 469 transaction.Save("this is point"); //定义命名存储点,使用Save方法先保存存储点,定义回滚数据的开始位置 470 //这边是你要回滚的操作代码,TO DO... 471 //把从命名存储点到这里的操作进行回滚 472 transaction.Rollback("this is point"); //回滚命名存储点 473 } 474 475 2、SQL语句中的事务 476 477 BEGIN TRANSACTION 478 --你需要执行的更新,删除,插入的语句 479 IF(@@ERROR > 0) //这是系统变量,存储你在执行更新,删除,插入操作时发生错误的记录编号 480 ROLLBACK 481 ELSE 482 COMMIT 483 484 3、TransactionScope 485 486 using (TransactionScope transactionScope = new TransactionScope()) 487 { 488 try 489 { 490 using (SqlConnection connection = new SqlConnection()) 491 { 492 // TO DO 493 //提交事务,如果有异常,他会自动回滚的 494 transactionScope.Complete(); 495 } 496 } 497 catch (Exception) 498 { 499 //捕获异常 500 throw; 501 } 502 } 503 504 七、SqlDataAdapter(数据适配器) 505 506 1、构造函数 507 508 四个重载: 509 510 (1)无参 511 512 (2)SqlDataAdapter(SqlCommand) 执行命令对象实例 513 514 (3)SqlDataAdapter(String,SqlConnection) 只能指定查询语句,连接对象实例 515 516 (4)SqlDataAdapter(String,ConnectionString) 517 518 2、填充数据(Fill) 519 520 DataSet dataSet = new DataSet(); 521 using (SqlConnection conn = new SqlConnection("")) 522 { 523 conn.Open(); 524 SqlCommand command = conn.CreateCommand(); 525 command.CommandText = "select name,age,address from MyInformation"; 526 SqlDataAdapter dataAdapter = new SqlDataAdapter(command); 527 dataAdapter.Fill(dataSet); //填充数据 528 } 529 530 531 3、使用“SqlCommandBuilder”对数据进行增删改查 532 (1)添加数据 533 534 using (SqlConnection conn = new SqlConnection(ConnectionString())) 535 { 536 conn.Open(); 537 //构建查询语句,也可以指定SqlCommand,其中变换的方法有很多 538 SqlDataAdapter da = new SqlDataAdapter("select LastName,FirstName from dbo.Employees", conn); 539 DataSet ds = new DataSet(); 540 da.Fill(ds); 541 //这句话很重要,它会把你在DataSet增加的数据转化为SQL语句用来更新数据库 542 SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(da); 543 //添加行,实例化一个行对象,注意是用NewRow来创建行 544 DataRow row = ds.Tables[0].NewRow(); 545 row[0] = "Yang"; 546 row[1] = "鬼头"; 547 ds.Tables[0].Rows.Add(row); //添加到表中 548 da.Update(ds); //把DataSet中表和数据库进行对比,更新 549 } 550 551 (2)修改数据 552 553 using (SqlConnection conn = new SqlConnection("")) 554 { 555 SqlDataAdapter da = new SqlDataAdapter("SQL语句或你自己定义的命令对象", conn); 556 DataSet ds = new DataSet(); 557 da.Fill(ds); 558 SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(da); 559 ds.Tables[0].Rows[12][1] = ""; //修改数据 560 da.Update(ds); 561 //调用Update方法其中隐式的调用了AcceptChanges方法,更新数据集中的数据 562 //如果你继续使用这个数据集而没有调用这个方法,在后面的使用会出现异常 563 ds.AcceptChanges(); //这句话可以不写的 564 } 565 566 (3)删除数据 567 568 using (SqlConnection conn = new SqlConnection("")) 569 { 570 SqlDataAdapter da = new SqlDataAdapter("SQL语句或你自己定义的命令对象", conn); 571 DataSet ds = new DataSet(); 572 da.Fill(ds); 573 SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(da); 574 //删除数据 575 ds.Tables[0].Rows[12].Delete(); 576 da.Update(ds); //这边会隐式调用DataTable的AcceptChanges方法 577 } 578 4、关于“SqlDataAdapter”中Fill方法 579 580 ds.Fill(ds,5,10,"MyTable"); 581 582 八、DataSet、DataTable、DataRow、DataColumn 583 584 表示数据存放在缓存中,DataSet里面可以包含多个DataTable,DataTable中有多个DataColumn和多个DataRow,包括对各种对DataTable的操作,以及对列和行的操作,在进行DataSet,DataTable进行操作的时候,应该先判断它们是否为Null。