当前位置: 移动技术网 > IT编程>开发语言>.net > 在ASP.NET 2.0中操作数据之六十五:在TableAdapters中创建新的存储过程

在ASP.NET 2.0中操作数据之六十五:在TableAdapters中创建新的存储过程

2017年12月12日  | 移动技术网IT编程  | 我要评论

吴亦凡退队,tip122中文资料,继承者们下载mp4

导言:

  本教程的data access layer (dal)使用的是类型化的数据集(typed datasets).就像我们在第一章《》里探讨的一样,该类型化的数据集由强类型的datatable和tableadapter构成。datatable描绘的是系统里的逻辑实体而tableadapter引用相关数据库执行数据访问,包括对datatable填充数据、执行返回标量数据(scalar data)的请求、添加,更新,删除数据库里的记录等.

  tableadapter执行的sql命令要么是某个特定的sql statements,比如select columnlist from tablename;要么是存储过程.本教程前面部分的tableadapter使用的是sql statements.不过很多开发者和数据库管理员基于安全、便于维护等方面的考虑,偏爱使用存储过程;不过也有的人出于灵活性的考虑偏爱使用sql statement.就我自己而言,我也偏向于存储过程.在前面的文章,出于简化的目的我选用的是sql statements.

  当定义一个新tableadapter或添加新方法时,使用tableadapter的设置向导,我们可以很容易的创建新的或使用现有的存储过程.在本文,我们将考察如何使用设置向导自动的生产存储过程。在下一章我们考察如何设置tableadapter的方法使用现有的或手动创建存储过程.

  注意:关于讨论到底使用存储过程还是使用sql statements的问题,可参考rob howard的博客文章《don't use stored procedures yet?》()和frans bouma的博客文章《stored procedures are bad, m'kay?》(

存储过程基础

  一个存储过程由一系列的t-sql statement组成,当调用该存储过程时就执行这些t-sql statement.存储过程可以接受0到多个输入参数,返回标量值、输出参数,或最常见的返回select查询值.

  注意:存储过程stored procedures也经常引用为“sprocs” or “sps”.

  可以使用t-sql statement语句create procedure来创建存储过程.比如下面的t-sql脚本创建了一个名为getproductsbycategoryid的存储过程,它有一个名为 @categoryid的参数,并且将表products里与categoryid值相吻合的那条记录的productid, productname, unitprice,以及discontinued值返回.

create procedure getproductsbycategoryid
(
 @categoryid int
)
as

select productid, productname, unitprice, discontinued
from products
where categoryid = @categoryid

创建后,我们可以用下面的代码调用它:

exec getproductsbycategory categoryid

  注意:在下篇文章我们将在visual studio ide集成环境里创建存储过程.不过在本文,我们将用tableadapter向导来自动创建存储过程.

  除了返回数据外,我们还可以在一个事务里用存储过程执行多条数据库命令.比如,假如有一个名为deletecategory的存储过程,其包含一个输入参数@categoryid,并执行2个delete statemets,第一个是删除相关的products,第二个是删除category。存储过程里面的多个statements并不是自动的封装在一个事务里的.我们应添加额外的t-sql commands以确保存储过程里的多条数据库命令当成原子操作处理.我们将在后面的内容考察如何用事务来封装存储过程的命令.

  当在体系的某个层使用存储过程时,data access layer的方法将调用某个具体的存储过程而不是发出一个sql statement命令.这样一来我们可以发现、分析发出的查询命令.并可以更清楚的看到数据库是如何使用的.有关存储过程基本原理的更多信息,可参考本文结束部分的延伸阅读.

第一步:创建数据访问层高级场景的web页面

在开始之前,让我们花点时间创建本文及后面几篇文章要用到的页面。新建一个名为advanceddal的文件夹,然后添加如下的asp.net页面,记得使用母版页site.master:

default.aspx
newsprocs.aspx
existingsprocs.aspx
joins.aspx
addingcolumns.aspx
computedcolumns.aspx
encryptingconfigsections.aspx
managedfunctionsandsprocs.aspx

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908019119.jpg
图1:添加相关的页面

像其它文件夹一样,default.aspx页面将列出本部分的内容,记得sectionleveltutoriallisting.ascx用户控件提供了该功能。因此,将其从解决资源管理器里拖放到default.aspx页面.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908017490.jpg
图2:将sectionleveltutoriallisting.ascx用户控件拖到default.aspx页面

最后,将这些页面添加到web.sitemap文件里。特别的,把下面的代码放在“working with batched data”

<sitemapnode>标签后面:

<sitemapnode url="~/advanceddal/default.aspx"
 title="advanced dal scenarios"
 description="explore a number of advanced data access layer scenarios.">
 
 <sitemapnode url="~/advanceddal/newsprocs.aspx"
 title="creating new stored procedures for tableadapters"
 description="learn how to have the tableadapter wizard automatically
 create and use stored procedures." />
 <sitemapnode url="~/advanceddal/existingsprocs.aspx"
 title="using existing stored procedures for tableadapters"
 description="see how to plug existing stored procedures into a
 tableadapter." />
 <sitemapnode url="~/advanceddal/joins.aspx"
 title="returning data using joins"
 description="learn how to augment your datatables to work with data
 returned from multiple tables via a join query." />
 <sitemapnode url="~/advanceddal/addingcolumns.aspx"
 title="adding datacolumns to a datatable"
 description="master adding new columns to an existing datatable." />
 <sitemapnode url="~/advanceddal/computedcolumns.aspx"
 title="working with computed columns"
 description="explore how to work with computed columns when using
 typed datasets." />
 <sitemapnode url="~/advanceddal/encryptingconfigsections.aspx"
 title="protected connection strings in web.config"
 description="protect your connection string information in
 web.config using encryption." />
 <sitemapnode url="~/advanceddal/managedfunctionsandsprocs.aspx"
 title="creating managed sql functions and stored procedures"
 description="see how to create sql functions and stored procedures
 using managed code." />
</sitemapnode>

更新web.sitemap文件后,花点时间在浏览器里查看,左边的菜单将包括本部分的内容.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908022063.jpg
图3:网站地图现在包含了不部分的页面

第二步:设置tableadapter创建新的存储过程

  我们在~/app_code/dal文件夹里创建一个类型化的dataset,名称为northwindwithsprocs.xsd.由于我们在以前的教程里已经详细探讨了创建细节,因此我们这里一笔带过,如果你想知道详细的创建过程请参阅前面的第1章《》在dal文件夹上右击鼠标选“添加新项”,选dataset模板,如图4所示.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908029119.jpg
图4:新建一个名为northwindwithsprocs.xsd的数据集

  这样将会创建一个新的类型化的dataset,打开设计器,创建一个新的tableadapter,展开tableadapter设置向导.向导的第一步是让我们选择要连接的数据库.在下拉列表里有一个连接到northwind数据库的连接字符串,选中它,再点下一步。接下来的界面让我们选择tableadapter以哪种方式访问数据库.在以前的教程里我们选择的是“use sql statements”,不过在本文我们选第二项:“create new stored procedures”,点下一步.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121907438091.jpg
图5:设置tableadpater创建新的存储过程

接下来,我们要指定主查询(main query).我们将创建一个存储过程来包含select查询.
使用下面的select查询:

select productid, productname, supplierid, categoryid,
 quantityperunit, unitprice, unitsinstock, unitsonorder,
 reorderlevel, discontinued
from products

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908035949.jpg
图6:键入select查询

  注意:在名为northwind的数据集里的productstableadapter的主查询与上面本文定义的主查询有所不同。那个主查询还返回了每个产品的category名称和company名称.不过在后面的文章我们将对本文的tableadapter添加这些相关的代码.再点“advanced options”按钮.我们可以指定是否让向导为tableadapter自动生成insert, update和delete statements;是否使用开发式并发操作(optimistic concurrency);是否完成inserts 和 update操作后刷新数据表.在默认情况下,自动选中“generate insert, update and delete statements”选项。另外,本文不用选择“use optimistic concurrency”项.当选择自动创建存储过程时,“refresh the data table”项将被忽略掉.不管是否选中该项,最终的insert 和update存储过程都会检索刚添加或刚更新(just-inserted or just-updated record)的记录,我们将在第三步看到.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908031464.jpg
图7:选中“generate insert, update and delete statements”项

  注意:当选中“use optimistic concurrency”项的时候,向导会在where语句里添加额外的条件,当其它列的值发生改动的话,将阻止数据更新.关于使用tableadapter内置的optimistic concurrency功能请参阅第21章《》输入select主查询并选取“generate insert, update and delete statements”项后,点下一步,接下来的界面,如图8所示,让我们为selecting, inserting, updating, 和deleting数据的存储过程命名.将这些存储过程的名字改为products_select, products_insert, products_update, 和products_delete.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908032750.jpg
图8:为存储过程重命名

向导创建了4个存储过程,点“preview sql script”按钮,你可以在preview sql script 对话框里将脚本保存在一个文件里或复制到剪贴板.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908034035.jpg
图9:预览生成的存储过程

  对存储过程重命名后,点下一步,对tableadapter相应的方法命名.就像使用sql statements一样,我们可以创建方法来填充一个现有的datatable或返回一个新的datatable;我们也一个指定tableadapter是否采用db-direct模式来插入、更新、删除记录.全选这3项,只不过将return a datatable方法重命名为getproducts,如图10所示:

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908032092.jpg
图10:将方法重命名为fill 和getproducts

点next总览向导将执行的步骤.点finish按钮完成设置.一旦向导结束后,将返回dataset设计器,它此时将包括productsdatatable.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908041621.jpg
图11:dataset设计器将显示刚刚添加的productsdatatable

第三步:考察刚刚创建的存储过程

  我们在第二步里用向导创建了选择、插入、更新、删除数据的存储过程.这些存储过程可以通过visual studio查看或修改.打开服务器资源管理器,点到数据库的存储过程文件夹。如图12所示,northwind数据库包含了4个新的存储过程,products_delete, products_insert, products_select, and products_update.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908046135.jpg
图12:可以在stored procedures文件夹里找到我们创建的4个存储过程

  注意:如果你看不到服务器资源管理器,点“view”菜单,选server explorer项.如果你无法找到新创建的存储过程,右击stored procedures文件夹,选“刷新”.

  要查看或修改某个存储过程,在服务器资源管理器里双击其名字或右击该存储过程,选”打开“。如13显示的是打开products_delete存储过程的画面.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908044192.jpg
图13:可以在visual studio里打开并修改存储过程

products_delete和products_select存储过程的内容很好理解。比如下面的代码构成了products_insert存储过程.

alter procedure dbo.products_insert
(
 @productname nvarchar(40),
 @supplierid int,
 @categoryid int,
 @quantityperunit nvarchar(20),
 @unitprice money,
 @unitsinstock smallint,
 @unitsonorder smallint,
 @reorderlevel smallint,
 @discontinued bit
)
as
 set nocount off;
insert into [products] ([productname], [supplierid], [categoryid], [quantityperunit],
 [unitprice], [unitsinstock], [unitsonorder], [reorderlevel], [discontinued])
values (@productname, @supplierid, @categoryid, @quantityperunit, @unitprice,
 @unitsinstock, @unitsonorder, @reorderlevel, @discontinued);
 
select productid, productname, supplierid, categoryid, quantityperunit, unitprice,
 unitsinstock, unitsonorder, reorderlevel, discontinued
from products
where (productid = scope_identity())

  在tableadapter向导里定义的select查询返回products表里的列,这些列又作为存储过程的输入参数并运用到insert statement中.紧接着的是一个select查询,返回products表里最新添加的记录的各列的值(包括productid)。当使用batch update模式添加一个新记录时,刷新功能是很有用的。因为它将最新添加的productrow instances实例的productid属性赋值为数据库指派的自增值.

  下面的代码说明了该功能.代码创建了基于northwindwithsprocs数据集的productstableadapter以及productsdatatable。要向数据库添加一个新的产品,我们要创建一个productsrow instance实例,对其赋值,并调用tableadapter的update方法,再传递给productsdatatable.在内部,tableadapter的update方法遍历传递给datatable的所有productsrow instance实例(在本例,只有一个。因为我们只添加了一个产品),并执行相应的insert, update, 或delete命令。此时,执行products_insert存储过程,其向products表添加一条新记录,并返回该记录的详细信息,然后更新productsrow instance实例的productid值。update方法完成后,我们就可以通过productsrow的productid属性访问新添加记录的productid值了.

// create the productstableadapter and productsdatatable
northwindwithsprocstableadapters.productstableadapter productsapi =
 new northwindwithsprocstableadapters.productstableadapter();
northwindwithsprocs.productsdatatable products =
 new northwindwithsprocs.productsdatatable();

// create a new productsrow instance and set its properties
northwindwithsprocs.productsrow product = products.newproductsrow();
product.productname = "new product";
product.categoryid = 1; // beverages
product.discontinued = false;

// add the productsrow instance to the datatable
products.addproductsrow(product);

// update the datatable using the batch update pattern
productsapi.update(products);

// at this point, we can determine the value of the newly-added record's productid
int newlyaddedproductidvalue = product.productid;

类似的,products_update存储过程的update statement后面也包含一个select statement,如下:

alter procedure dbo.products_update
(
 @productname nvarchar(40),
 @supplierid int,
 @categoryid int,
 @quantityperunit nvarchar(20),
 @unitprice money,
 @unitsinstock smallint,
 @unitsonorder smallint,
 @reorderlevel smallint,
 @discontinued bit,
 @original_productid int,
 @productid int
)
as
 set nocount off;
update [products]
set [productname] = @productname, [supplierid] = @supplierid,
 [categoryid] = @categoryid, [quantityperunit] = @quantityperunit,
 [unitprice] = @unitprice, [unitsinstock] = @unitsinstock,
 [unitsonorder] = @unitsonorder, [reorderlevel] = @reorderlevel,
 [discontinued] = @discontinued
where (([productid] = @original_productid));
 
select productid, productname, supplierid, categoryid, quantityperunit,
 unitprice, unitsinstock, unitsonorder, reorderlevel, discontinued
from products
where (productid = @productid)

  我们注意到该存储过程有2个关于productid的参数,即@original_productid 和@productid,这样以来我们就可以对主键值进行改动了.举个例子:有一个employee(雇员)数据库,每条employee记录都用雇员的社保号码作为其主键值.要想更改某条记录的社保号码,必须提供新的号码以及原始号码.不过对products表来说用不着,因为列productid是一个唯一标识列(identity column),不应对其更改.实际上,products_update存储过程里的update statement并没有包含productid列,因此,如果在update statement的where字句里使用@original_productid的话,显得多此一举,而应该使用@productid参数.当更新某个存储过程的参数时,tableadapter里所有那些调用该存储过程方法都应该进行更新.

第四步:修改存储过程的参数并更新tableadapter

  由于@original_productid参数是多余的,让我们将其从products_update存储过程里完全清除.打开products_update存储过程,删除@original_productid参数,在update statement的where字句里将@original_productid改为@productid. 完成上述修改后,该存储过程里的t-sql看起来应该和下面的差不多:

alter procedure dbo.products_update
(
 @productname nvarchar(40),
 @supplierid int,
 @categoryid int,
 @quantityperunit nvarchar(20),
 @unitprice money,
 @unitsinstock smallint,
 @unitsonorder smallint,
 @reorderlevel smallint,
 @discontinued bit,
 @productid int
)
as
 set nocount off;
update [products] set [productname] = @productname, [supplierid] = @supplierid,
 [categoryid] = @categoryid, [quantityperunit] = @quantityperunit,
 [unitprice] = @unitprice, [unitsinstock] = @unitsinstock,
 [unitsonorder] = @unitsonorder, [reorderlevel] = @reorderlevel,
 [discontinued] = @discontinued
where (([productid] = @productid));
 
select productid, productname, supplierid, categoryid, quantityperunit,
 unitprice, unitsinstock, unitsonorder, reorderlevel, discontinued
from products
where (productid = @productid)

  按ctrl+s或点工具栏里的“保存”图标,保存更改.此时,products_update存储过程不会执行@original_productid参数,但tableadapter仍然会传递该参数.要想查看tableadapter传递给products_update存储过程的参数,你可以在设计器里选中tableadapter,转到属性窗口,点更新命令的参数集(updatecommand'sparameters collection)里的椭圆型区域,这样将转到parameters collection editor对话框,如图14所示:

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908041464.jpg
图14:对话框里列出了传递给products_update存储过程的参数

要删除参数,只需选中它,再点remove按钮.

  要刷新参数的话,你也可以在设计器里选中tableadapter,点右键选“设置”,这将会开启tableadapter设置向导,它列出了用于select, insert, updat和delete的存储过程,并列出了这些存储过程的输入参数.如果你在update下拉列表里选products_update的话,你可以看到该存储过程包含的输入参数里已经没有包含@original_productid了(见图15),点finish将对tableadapter使用的参数集自动更新.

 http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908045978.jpg
图15:你可以通过使用tableadapter的设置向导来刷新参数集

第五步:添加额外的tableadapter方法

  我们在第二步说过,当创建一个新的tableadapter时,很容易自动地生成相应的存储过程,同样我们也可以向tableadapter添加额外的方法.作为演示,让我们向productstableadapter添加一个方法getproductbyproductid(productid),该方法将一个productid作为输入参数,并返回该产品的详细信息.在productstableadapter上点击右键,选择“添加查询”.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908055507.jpg
图16:向tableadapter添加新查询

  这将开启tableadapter查询设置向导。首先,向导将询问以何种方式访问数据库,我们将创建一个新的存储过程,因此选“create a new stored procedure”,再点next.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121907438091.jpg
图17:选中“create a new stored procedure”项

  接下来,向导询问我们执行哪种查询,是返回一系列行?一个标量值?又或者执行update, insert,或 delete statement.由于getproductbyproductid(productid)方法将返回一行,我们选择“select which returns row”项,再点next.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121907448464.jpg
图18:选择“select which returns row” 项

  接下来的界面将展示tableadapter的主查询,其仅仅列出了存储过程的名字(也就是dbo.products_select).将其删除,替换为如下的select statement,它返回某个具体产品的所有列.

select productid, productname, supplierid, categoryid,
 quantityperunit, unitprice, unitsinstock, unitsonorder,
 reorderlevel, discontinued
from products
where productid = @productid

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908052809.jpg
图19:将存储过程的名字替换为一个select查询.

  接下来要对创建的存储过程命名,输入products_selectbyproductid,点next.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908062180.jpg
图20:将新存储过程命名为products_selectbyproductid

  最后一步将要我们对自动生成的名字重新命名,并指定是否使用fill a datatable模式、是否使用return a datatable模式,抑或这2种模式都采用.就本文而言,都选中这2项并将方法重命名为fillbyproductid 和 getproductbyproductid.点next,再点finish完成设置向导.

 http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908069236.jpg

图21:将tableadapter的方法重命名为fillbyproductid 和 getproductbyproductid

  完成向导后,tableadapter将包含一个新的可用方法——getproductbyproductid(productid),当调用该方法时,将执行我们刚刚创建的products_selectbyproductid存储过程.花点时间在服务器资源管理器里查看该存储过程,点stored procedures文件夹,并打开products_selectbyproductid(如果你没看到它,在stored procedures文件夹上右击鼠标,选“刷新”).

  请注意,selectbyproductid存储过程将@productid作为输入参数,并执行我们在向导里输入的select statement,如下:

alter procedure dbo.products_selectbyproductid
(
 @productid int
)
as
 set nocount on;

select productid, productname, supplierid, categoryid,
 quantityperunit, unitprice, unitsinstock, unitsonorder,
 reorderlevel, discontinued
from products
where productid = @productid

第六步:创建一个业务逻辑层类

在我们打算从表现层访问产品前,我们首先需要为新添加的数据集创建一个bll class,在~/app_code/bll文件夹里创建一个productsbllwithsprocs.cs文件,如下:

using system;
using system.data;
using system.configuration;
using system.web;
using system.web.security;
using system.web.ui;
using system.web.ui.webcontrols;
using system.web.ui.webcontrols.webparts;
using system.web.ui.htmlcontrols;
using northwindwithsprocstableadapters;

[system.componentmodel.dataobject]
public class productsbllwithsprocs
{
 private productstableadapter _productsadapter = null;
 protected productstableadapter adapter
 {
 get
 {
  if (_productsadapter == null)
  _productsadapter = new productstableadapter();

  return _productsadapter;
 }
 }

 [system.componentmodel.dataobjectmethodattribute
 (system.componentmodel.dataobjectmethodtype.select, true)]
 public northwindwithsprocs.productsdatatable getproducts()
 {
 return adapter.getproducts();
 }


 [system.componentmodel.dataobjectmethodattribute
 (system.componentmodel.dataobjectmethodtype.select, false)]
 public northwindwithsprocs.productsdatatable getproductbyproductid(int productid)
 {
 return adapter.getproductbyproductid(productid);
 }


 [system.componentmodel.dataobjectmethodattribute
 (system.componentmodel.dataobjectmethodtype.insert, true)]
 public bool addproduct
 (string productname, int? supplierid, int? categoryid,
  string quantityperunit, decimal? unitprice, short? unitsinstock,
  short? unitsonorder, short? reorderlevel, bool discontinued)
 {
 // create a new productrow instance
 northwindwithsprocs.productsdatatable products =
  new northwindwithsprocs.productsdatatable();
 northwindwithsprocs.productsrow product = products.newproductsrow();

 product.productname = productname;
 if (supplierid == null)
  product.setsupplieridnull();
 else
  product.supplierid = supplierid.value;
 if (categoryid == null)
  product.setcategoryidnull();
 else
  product.categoryid = categoryid.value;
 if (quantityperunit == null)
  product.setquantityperunitnull();
 else
  product.quantityperunit = quantityperunit;
 if (unitprice == null)
  product.setunitpricenull();
 else
  product.unitprice = unitprice.value;
 if (unitsinstock == null)
  product.setunitsinstocknull();
 else
  product.unitsinstock = unitsinstock.value;
 if (unitsonorder == null)
  product.setunitsonordernull();
 else
  product.unitsonorder = unitsonorder.value;
 if (reorderlevel == null)
  product.setreorderlevelnull();
 else
  product.reorderlevel = reorderlevel.value;
 product.discontinued = discontinued;

 // add the new product
 products.addproductsrow(product);
 int rowsaffected = adapter.update(products);

 // return true if precisely one row was inserted, otherwise false
 return rowsaffected == 1;
 }

 [system.componentmodel.dataobjectmethodattribute
 (system.componentmodel.dataobjectmethodtype.update, true)]
 public bool updateproduct
 (string productname, int? supplierid, int? categoryid, string quantityperunit,
 decimal? unitprice, short? unitsinstock, short? unitsonorder,
 short? reorderlevel, bool discontinued, int productid)
 {
 northwindwithsprocs.productsdatatable products =
  adapter.getproductbyproductid(productid);
 if (products.count == 0)
  // no matching record found, return false
  return false;

 northwindwithsprocs.productsrow product = products[0];

 product.productname = productname;
 if (supplierid == null)
  product.setsupplieridnull();
 else
  product.supplierid = supplierid.value;
 if (categoryid == null)
  product.setcategoryidnull();
 else
  product.categoryid = categoryid.value;
 if (quantityperunit == null)
  product.setquantityperunitnull();
 else
  product.quantityperunit = quantityperunit;
 if (unitprice == null)
  product.setunitpricenull();
 else
  product.unitprice = unitprice.value;
 if (unitsinstock == null)
  product.setunitsinstocknull();
 else
  product.unitsinstock = unitsinstock.value;
 if (unitsonorder == null)
  product.setunitsonordernull();
 else
  product.unitsonorder = unitsonorder.value;
 if (reorderlevel == null)
  product.setreorderlevelnull();
 else
  product.reorderlevel = reorderlevel.value;
 product.discontinued = discontinued;

 // update the product record
 int rowsaffected = adapter.update(product);

 // return true if precisely one row was updated, otherwise false
 return rowsaffected == 1;
 }

 [system.componentmodel.dataobjectmethodattribute
 (system.componentmodel.dataobjectmethodtype.delete, true)]
 public bool deleteproduct(int productid)
 {
 int rowsaffected = adapter.delete(productid);

 // return true if precisely one row was deleted, otherwise false
 return rowsaffected == 1;
 }
}

  该类和以前章节所创建的productsbll class类差不多,只是它用的是数据集 northwindwithsprocs的productstableadapter 和 productsdatatable object对象。与productsbll类使用using northwindtableadapters不同,productsbllwithsprocs类使用的是using northwindwithsprocstableadapters.同样的,该类的productsdatatable和 productsrow对象使用的是northwindwithsprocs命名空间.我们的productsbllwithsprocs class类提供了2种数据访问方法getproducts() 和getproductbyproductid().另外,还有添加、更新、删除单个产品的方法.

第七步:在表现层出来数据集northwindwithsprocs

  此时,我们以及对数据访问层和业务逻辑层做了相关改动,接下来我们要创建一个asp.net页面调用bll的productsbllwithsprocs class类以展示、更新、删除记录.

  打开advanceddal文件夹里的newsprocs.aspx页面,从工具箱拖一个gridview控件到页面,设置其id为products. 从gridview的智能标签将其绑定到一个名为productsdatasource的objectdatasource,设置其调用productsbllwithsprocs类.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908067980.jpg
图22:设置objectdatasource调用productsbllwithsprocs类

  select标签的下拉列表里有2个方法,getproducts()和getproductbyproductid().由于我们将在gridview里显示所有的产品,所以我们选getproducts()方法.在update, insert, 和delete标签里都只有一个方法,确保选中它们,点finish按钮。

  完成设置后,visual studio会向gridview添加boundfields列以及一个checkboxfield列, 启用gridview控件的“编辑”和“删除”功能.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908061051.jpg
图23:页面包含一个可以分页和排序的gridview控件.

  就像在以前的教程里探讨过的一样,完成objectdatasource的设置后,visual studio 会自动的将oldvaluesparameterformatstring属性设置为“original_{0}”. 为使数据修改功能正常工作,要么将该属性删除,要么将其设置为“{0}”.

  在我们完成设置、启用“编辑”和“删除”功能、将oldvaluesparameterformatstring属性设为其默认值后,页面的声明代码看起来应该和下面的差不多:

<asp:gridview id="products" runat="server" autogeneratecolumns="false"
 datakeynames="productid" datasourceid="productsdatasource">
 <columns>
 <asp:commandfield showdeletebutton="true" showeditbutton="true" />
 <asp:boundfield datafield="productid" headertext="productid"
  insertvisible="false" readonly="true"
  sortexpression="productid" />
 <asp:boundfield datafield="productname" headertext="productname"
  sortexpression="productname" />
 <asp:boundfield datafield="supplierid" headertext="supplierid"
  sortexpression="supplierid" />
 <asp:boundfield datafield="categoryid" headertext="categoryid"
  sortexpression="categoryid" />
 <asp:boundfield datafield="quantityperunit" headertext="quantityperunit"
  sortexpression="quantityperunit" />
 <asp:boundfield datafield="unitprice" headertext="unitprice"
  sortexpression="unitprice" />
 <asp:boundfield datafield="unitsinstock" headertext="unitsinstock"
  sortexpression="unitsinstock" />
 <asp:boundfield datafield="unitsonorder" headertext="unitsonorder"
  sortexpression="unitsonorder" />
 <asp:boundfield datafield="reorderlevel" headertext="reorderlevel"
  sortexpression="reorderlevel" />
 <asp:checkboxfield datafield="discontinued" headertext="discontinued"
  sortexpression="discontinued" />
 </columns>
</asp:gridview>

<asp:objectdatasource id="productsdatasource" runat="server"
 deletemethod="deleteproduct" insertmethod="addproduct"
 selectmethod="getproducts" typename="productsbllwithsprocs"
 updatemethod="updateproduct">
 <deleteparameters>
 <asp:parameter name="productid" type="int32" />
 </deleteparameters>
 <updateparameters>
 <asp:parameter name="productname" type="string" />
 <asp:parameter name="supplierid" type="int32" />
 <asp:parameter name="categoryid" type="int32" />
 <asp:parameter name="quantityperunit" type="string" />
 <asp:parameter name="unitprice" type="decimal" />
 <asp:parameter name="unitsinstock" type="int16" />
 <asp:parameter name="unitsonorder" type="int16" />
 <asp:parameter name="reorderlevel" type="int16" />
 <asp:parameter name="discontinued" type="boolean" />
 <asp:parameter name="productid" type="int32" />
 </updateparameters>
 <insertparameters>
 <asp:parameter name="productname" type="string" />
 <asp:parameter name="supplierid" type="int32" />
 <asp:parameter name="categoryid" type="int32" />
 <asp:parameter name="quantityperunit" type="string" />
 <asp:parameter name="unitprice" type="decimal" />
 <asp:parameter name="unitsinstock" type="int16" />
 <asp:parameter name="unitsonorder" type="int16" />
 <asp:parameter name="reorderlevel" type="int16" />
 <asp:parameter name="discontinued" type="boolean" />
 </insertparameters>
</asp:objectdatasource>

  此时,我们可以对gridview控件做些修改,比如在编辑界面里使用确认控件,在categoryid 和 supplierid列放置dropdownlist控件,当点击delete按钮时弹出确认框等.由于在以前的教程我们探讨过这些主题,我不打算在此多花笔墨。

  不管你做没做这些改进,让我们在浏览器里对页面测试,如图24所示.在gridview控件里每行都可以编辑和删除.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908063809.jpg
图24:可以通过gridview对产品进行查看、编辑、删除

结语:

  类型化数据集里的tableadapters可以通过ad-hoc sql statement或存储过程访问数据库里的数据.当处理存储过程时,我们要么使用现有的存储过程,要么使用tableadapter向导创建一个基于select查询的新的存储过程.在本文,我们考察了如何自动的创建一个存储过程.

  虽然自动创建可以节省时间,但是在某些情况下,向导自动创建的存储过程与我们的期望值还是有差距.比如自动创建的products_update存储过程,它包含@original_productid 和 @productid这2个参数,但@original_productid参数对我们来说是多余的.

在接下来的文章,我们将考察tableadapter使用现有的存储过程的情况.

  祝编程快乐!

作者简介

  本系列教程作者 scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。大家可以点击查看全部教程《[翻译]scott mitchell 的asp.net 2.0数据教程》,希望对大家的学习asp.net有所帮助。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网