以下代码阐明了如何使用 sqldataadapter 对象发出可生成 dataset 或 datatable 的命令。它从 sql server northwind 数据库中检索一组产品类别。
using system.data;using system.data.sqlclient;public datatable retrieverowswithdatatable(){ using ( sqlconnection conn = new sqlconnection(connectionstring) ) { conn.open(); sqlcommand cmd = new sqlcommand("datretrieveproducts", conn); cmd.commandtype = commandtype.storedprocedure; sqldataadapter adapter = new sqldataadapter( cmd ); datatable datatable = new datatable("products"); adapter .fill(datatable); return datatable; }}
1. | 创建一个 sqlcommand 对象以调用该存储过程,并将其与一个 sqlconnection 对象(显示)或连接字符串(不显示)相关联。 |
2. | 创建一个新的 sqldataadapter 对象并将其与 sqlcommand 对象相关联。 |
3. | 创建一个 datatable(也可以创建一个 dataset)对象。使用构造函数参数来命名 datatable。 |
4. | 调用 sqldataadapter 对象的 fill 方法,用检索到的行填充 dataset 或 datatable。 |
以下代码片段阐明了可检索多个行的 sqldatareader 方法。
using system.io;using system.data;using system.data.sqlclient;public sqldatareader retrieverowswithdatareader(){ sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=northwind"); sqlcommand cmd = new sqlcommand("datretrieveproducts", conn ); cmd.commandtype = commandtype.storedprocedure; try { conn.open(); // generate the reader. commandbehavior.closeconnection causes the // the connection to be closed when the reader object is closed return( cmd.executereader( commandbehavior.closeconnection ) ); } catch { conn.close(); throw; }}// display the product list using the consoleprivate void displayproducts(){ sqldatareader reader = retrieverowswithdatareader(); try { while (reader.read()) { console.writeline("{0} {1} {2}", reader.getint32(0).tostring(), reader.getstring(1) ); } } finally { reader.close(); // also closes the connection due to the // commandbehavior enum used when generating the reader }}
1. | 创建一个用来执行存储过程的 sqlcommand 对象,并将其与一个 sqlconnection 对象相关联。 |
2. | 打开连接。 |
3. | 通过调用 sqlcommand 对象的 executereader 方法创建一个 sqldatareader 对象。 |
4. | 要从流中读取数据,请调用 sqldatareader 对象的 read 方法来检索行,并使用类型化访问器方法(如 getint32 和 getstring 方法)来检索列值。 |
5. | 使用完读取器后,请调用其 close 方法。 |
可以使用 sqlcommand 对象来生成 xmlreader 对象,后者可提供对 xml 数据的基于流的只进访问。命令(通常为存储过程)必须产生基于 xml 的结果集,对于 sql server 2000 而言,该结果集通常包含一个带有有效 for xml 子句的 select 语句。以下代码片段阐明了该方法:
public void retrieveanddisplayrowswithxmlreader(){ using( sqlconnection conn = new sqlconnection(connectionstring) ) {; sqlcommand cmd = new sqlcommand("datretrieveproductsxml", conn ); cmd.commandtype = commandtype.storedprocedure;try { conn.open(); xmltextreader xreader = (xmltextreader)cmd.executexmlreader(); while ( xreader.read() ) { if ( xreader.name == "products" ) { string stroutput = xreader.getattribute("productid"); stroutput += " "; stroutput += xreader.getattribute("productname"); console.writeline( stroutput ); } } xreader.close(); // xmltextreader does not support idisposable so it can't be // used within a using keyword }}
上述代码使用了以下存储过程:
create procedure datretrieveproductsxmlasselect * from products for xml autogo
1. | 创建一个 sqlcommand 对象来调用可生成 xml 结果集的存储过程(例如,在 select 语句中使用 for xml 子句)。将该 sqlcommand 对象与某个连接相关联。 |
2. | 调用 sqlcommand 对象的 executexmlreader 方法,并且将结果分配给只进 xmltextreader 对象。当您不需要对返回的数据进行任何基于 xml 的验证时,这是应该使用的最快类型的 xmlreader 对象。 |
3. | 使用 xmltextreader 对象的 read 方法来读取数据。 |
借助于命名的输出参数,可以调用在单个行内返回检索到的数据项的存储过程。以下代码片段使用存储过程来检索 northwind 数据库的 products 表中包含的特定产品的产品名称和单价。
void getproductdetails( int productid, out string productname, out decimal unitprice ){ using( sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=northwind") ) { // set up the command object used to execute the stored proc sqlcommand cmd = new sqlcommand( "datgetproductdetailsspoutput", conn ) cmd.commandtype = commandtype.storedprocedure; // establish stored proc parameters. // @productid int input // @productname nvarchar(40) output // @unitprice money output // must explicitly set the direction of output parameters sqlparameter paramprodid = cmd.parameters.add( "@productid", productid ); paramprodid.direction = parameterdirection.input; sqlparameter paramprodname = cmd.parameters.add( "@productname", sqldbtype.varchar, 40 ); paramprodname.direction = parameterdirection.output; sqlparameter paramunitprice = cmd.parameters.add( "@unitprice", sqldbtype.money ); paramunitprice.direction = parameterdirection.output; conn.open(); // use executenonquery to run the command. // although no rows are returned any mapped output parameters // (and potentially return values) are populated cmd.executenonquery( ); // return output parameters from stored proc productname = paramprodname.value.tostring(); unitprice = (decimal)paramunitprice.value; }}
1. | 创建一个 sqlcommand 对象并将其与一个 sqlconnection 对象相关联。 |
2. | 通过调用 sqlcommand 的 parameters 集合的 add 方法来设置存储过程参数。默认情况下,参数都被假设为输入参数,因此必须显式设置任何输出参数的方向。 注 一种良好的习惯做法是显式设置所有参数(包括输入参数)的方向。 |
3. | 打开连接。 |
4. | 调用 sqlcommand 对象的 executenonquery 方法。这将填充输出参数(并可能填充返回值)。 |
5. | 通过使用 value 属性,从适当的 sqlparameter 对象中检索输出参数。 |
6. | 关闭连接。 |
上述代码片段调用了以下存储过程。
create procedure [email protected] int,@productname nvarchar(40) output,@unitprice money outputasselect @productname = productname, @unitprice = unitprice from products where productid = @productidgo
可以使用 sqldatareader 对象来检索单个行,尤其是可以从返回的数据流中检索需要的列值。以下代码片段对此进行了说明。
void getproductdetailsusingreader( int productid, out string productname, out decimal unitprice ){ using( sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=northwind") ) { // set up the command object used to execute the stored proc sqlcommand cmd = new sqlcommand( "datgetproductdetailsreader", conn ); cmd.commandtype = commandtype.storedprocedure; // establish stored proc parameters. // @productid int input sqlparameter paramprodid = cmd.parameters.add( "@productid", productid ); paramprodid.direction = parameterdirection.input; conn.open(); using( sqldatareader reader = cmd.executereader() ) { if( reader.read() ) // advance to the one and only row { // return output parameters from returned data stream productname = reader.getstring(0); unitprice = reader.getdecimal(1); } } }}
1. | 建立 sqlcommand 对象。 |
2. | 打开连接。 |
3. | 调用 sqldatareader 对象的 executereader 方法。 |
4. | 通过 sqldatareader 对象的类型化访问器方法(在这里,为 getstring 和 getdecimal)来检索输出参数。 |
上述代码片段调用了以下存储过程。
create procedure [email protected] intasselect productname, unitprice from productswhere productid = @productidgo
executescalar 方法专门适用于仅返回单个值的查询。如果查询返回多个列和/或行,executescalar 将只返回第一行的第一列。
以下代码说明了如何查找与特定产品 id 相对应的产品名称:
void getproductnameexecutescalar( int productid, out string productname ){ using( sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=northwind") ) { sqlcommand cmd = new sqlcommand("lookupproductnamescalar", conn ); cmd.commandtype = commandtype.storedprocedure; cmd.parameters.add("@productid", productid ); conn.open(); productname = (string)cmd.executescalar(); }}
1. | 建立一个 sqlcommand 对象来调用存储过程。 |
2. | 打开连接。 |
3. | 调用 executescalar 方法。注意,该方法返回一个对象类型。它包含检索到的第一列的值,并且必须转化为适当的类型。 |
4. | 关闭连接。 |
上述代码使用了以下存储过程:
create procedure [email protected] intasselect top 1 productnamefrom productswhere productid = @productidgo
可以使用存储过程输出或返回参数来查找单个值。以下代码阐明了输出参数的用法:
void getproductnameusingspoutput( int productid, out string productname ){ using( sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=northwind") ) { sqlcommand cmd = new sqlcommand("lookupproductnamespoutput", conn ); cmd.commandtype = commandtype.storedprocedure; sqlparameter paramprodid = cmd.parameters.add("@productid", productid ); paramprodid.direction = parameterdirection.input; sqlparameter parampn = cmd.parameters.add("@productname", sqldbtype.varchar, 40 ); parampn.direction = parameterdirection.output; conn.open(); cmd.executenonquery(); productname = parampn.value.tostring(); }}
1. | 建立一个 sqlcommand 对象来调用存储过程。 |
2. | 通过将 sqlparameters 添加到 sqlcommand 的 parameters 集合中,设置任何输入参数和单个输出参数。 |
3. | 打开连接。 |
4. | 调用 sqlcommand 对象的 executenonquery 方法。 |
5. | 关闭连接。 |
6. | 通过使用输出 sqlparameter 的 value 属性来检索输出值。 |
上述代码使用了以下存储过程。
create procedure lookupproductnamespoutput @productid int,@productname nvarchar(40) outputasselect @productname = productnamefrom productswhere productid = @productidgo
以下代码阐明了如何使用返回值来指明是否存在特定行。从编码角度来看,这类似于使用存储过程输出参数,不同之处在于必须将 sqlparameter 方向显式设置为 parameterdirection.returnvalue。
bool checkproduct( int productid ){ using( sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=northwind") ) { sqlcommand cmd = new sqlcommand("checkproductsp", conn ); cmd.commandtype = commandtype.storedprocedure; cmd.parameters.add("@productid", productid ); sqlparameter paramret = cmd.parameters.add("@productexists", sqldbtype.int ); paramret.direction = parameterdirection.returnvalue; conn.open(); cmd.executenonquery(); } return (int)paramret.value == 1;}
1. | 建立一个 sqlcommand 对象来调用存储过程。 |
2. | 设置一个输入参数,该参数含有要访问的行的主键值。 |
3. | 设置单个返回值参数。将一个 sqlparameter 对象添加到 sqlcommand 的 parameters 集合中,并将其方向设置为 parameterdirection.returnvalue。 |
4. | 打开连接。 |
5. | 调用 sqlcommand 对象的 executenonquery 方法。 |
6. | 关闭连接。 |
7. | 通过使用返回值 sqlparameter 的 value 属性来检索返回值。 |
上述代码使用了以下存储过程。
create procedure checkproductsp @productid intasif exists( select productid from products where productid = @productid ) return 1else return 0go
可以使用 sqldatareader 对象并通过调用命令对象的 executereader 方法来获取单个输出值。这要求编写稍微多一点的代码,因为必须调用 sqldatareader read 方法,然后通过该读取器的访问器方法之一来检索需要的值。以下代码阐明了 sqldatareader 对象的用法。
bool checkproductwithreader( int productid ){ using( sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=northwind") ) { sqlcommand cmd = new sqlcommand("checkproductexistswithcount", conn ); cmd.commandtype = commandtype.storedprocedure; cmd.parameters.add("@productid", productid ); cmd.parameters["@productid"].direction = parameterdirection.input; conn.open(); using( sqldatareader reader = cmd.executereader( commandbehavior.singleresult ) ) { if( reader.read() ) { return (reader.getint32(0) > 0); } return false; }}
上述代码采用了以下存储过程。
create procedure checkproductexistswithcount @productid intasselect count(*) from productswhere productid = @productidgo
以下代码显示了如何充分利用 sql server .net 数据提供程序所提供的事务处理支持,通过事务来保护资金转帐操作。该操作在同一数据库中的两个帐户之间转移资金。
public void transfermoney( string toaccount, string fromaccount, decimal amount ){ using ( sqlconnection conn = new sqlconnection( "server=(local);integrated security=sspi;database=simplebank" ) ) { sqlcommand cmdcredit = new sqlcommand("credit", conn ); cmdcredit.commandtype = commandtype.storedprocedure; cmdcredit.parameters.add( new sqlparameter("@accountno", toaccount) ); cmdcredit.parameters.add( new sqlparameter("@amount", amount )); sqlcommand cmddebit = new sqlcommand("debit", conn ); cmddebit.commandtype = commandtype.storedprocedure; cmddebit.parameters.add( new sqlparameter("@accountno", fromaccount) ); cmddebit.parameters.add( new sqlparameter("@amount", amount )); conn.open(); // start a new transaction using ( sqltransaction trans = conn.begintransaction() ) { // associate the two command objects with the same transaction cmdcredit.transaction = trans; cmddebit.transaction = trans; try { cmdcredit.executenonquery(); cmddebit.executenonquery(); // both commands (credit and debit) were successful trans.commit(); } catch( exception ex ) { // transaction failed trans.rollback(); // log exception details . . . throw ex; } } }}
以下存储过程阐明了如何在 transact-sql 存储过程内部执行事务性资金转帐操作。
create procedure [email protected] char(20),@toaccount char(20),@amount moneyasbegin transaction-- perform debit operationupdate accountsset balance = balance - @amountwhere accountnumber = @fromaccountif @@rowcount = 0begin raiserror('invalid from account number', 11, 1) goto abortenddeclare @balance moneyselect @balance = balance from accountswhere accountnumber = @fromaccountif @balance < 0begin raiserror('insufficient funds', 11, 1) goto abortend-- perform credit operationupdate accounts set balance = balance + @amount where accountnumber = @toaccountif @@rowcount = 0begin raiserror('invalid to account number', 11, 1) goto abortendcommit transactionreturn 0abort: rollback transactiongo
该存储过程使用 begin transaction、commit transaction 和 rollback transaction 语句来手动控制该事务。
以下示例代码显示了三个服务性 .net 托管类,这些类经过配置以执行自动事务处理。每个类都使用 transaction 属性进行了批注,该属性的值确定是否应该启动新的事务流,或者该对象是否应该共享其直接调用方的事务流。这些组件协同工作来执行银行资金转帐任务。transfer 类被使用 requiresnew 事务属性进行了配置,而 debit 和 credit 被使用 required 进行了配置。结果,所有这三个对象在运行时都将共享同一事务。
using system;using system.enterpriseservices;[transaction(transactionoption.requiresnew)]public class transfer : servicedcomponent{ [autocomplete] public void transfer( string toaccount, string fromaccount, decimal amount ) { try { // perform the debit operation debit debit = new debit(); debit.debitaccount( fromaccount, amount ); // perform the credit operation credit credit = new credit(); credit.creditaccount( toaccount, amount ); } catch( sqlexception sqlex ) { // handle and log exception details // wrap and propagate the exception throw new transferexception( "transfer failure", sqlex ); } }}[transaction(transactionoption.required)]public class credit : servicedcomponent{ [autocomplete] public void creditaccount( string account, decimal amount ) { try { using( sqlconnection conn = new sqlconnection( "server=(local); integrated security=sspi"; database="simplebank") ) { sqlcommand cmd = new sqlcommand("credit", conn ); cmd.commandtype = commandtype.storedprocedure; cmd.parameters.add( new sqlparameter("@accountno", account) ); cmd.parameters.add( new sqlparameter("@amount", amount )); conn.open(); cmd.executenonquery(); } } }catch( sqlexception sqlex ){ // log exception details here throw; // propagate exception }}[transaction(transactionoption.required)]public class debit : servicedcomponent{ public void debitaccount( string account, decimal amount ) { try { using( sqlconnection conn = new sqlconnection( "server=(local); integrated security=sspi"; database="simplebank") ) { sqlcommand cmd = new sqlcommand("debit", conn ); cmd.commandtype = commandtype.storedprocedure; cmd.parameters.add( new sqlparameter("@accountno", account) ); cmd.parameters.add( new sqlparameter("@amount", amount )); conn.open(); cmd.executenonquery(); } } catch (sqlexception sqlex) { // log exception details here throw; // propagate exception back to caller } }}注册会员,创建你的web开发资料库,
新闻热点
疑难解答
图片精选