热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在ASP.NET2.0中操作数据之六十六:在TableAdapters中使用现有的存储过程

虽然通过TableAdapter向导可以自动的生成存储过程,但是在某些时候我们需要使用现有的存储过程。本文将讲解如何在VisualStudio环境里手动添加存储过程,并引导TableAdapter的方法使用这些存储过程。

导言:

  在前面的文章里我们考察了如何让TableAdapters向导自动的创建存储过程.而在本文,我们将考察如何让TableAdapter使用现有的存储过程。由于Northwind数据库现有的存储过程很少,我们也需要考察如何在Visual Studio环境里手动向数据库添加新的存储过程.

  注意:在第61章《在事务里对数据库修改进行封装》里我们向TableAdapter添加了一些方法以支持事务(比如 (BeginTransaction, CommitTransaction等)。我们可以在不修改数据访问层代码的情况下,在一个存储过程里管理整个事务.在本文,我们还将对事务里执行存储过程的T-SQL commands命令进行考察.

第一步:向Northwind数据库添加存储过程

  我们很容易通过Visual Studio向数据库添加存储过程.让我们向Northwind数据库添加一个新存储过程,它返回Products表里特定CategoryID值的产品.在服务器资源管理窗口,展开Northwind数据库,就像我们在前面的文章看到的一样,存储过程文件夹包含了现有的存储过程。要添加新的存储过程的话,只需要右键单击存储过程文件夹,选“添加新存储过程”项,

//img.jbzj.com/file_images/article/201605/201605190901551.png
图1:右击Stored Procedures文件夹选“Add a New Stored Procedure”

如图1所示,选“Add a New Stored Procedure”项后,将在Visual Studio里打开一个脚本窗口.输入如下的脚本:

CREATE PROCEDURE dbo.Products_SelectByCategoryID
(
 @CategoryID int
)
AS

SELECT ProductID, ProductName, SupplierID, CategoryID,
 QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
 ReorderLevel, Discontinued
FROM Products
WHERE CategoryID = @CategoryID

  当执行该脚本时,将会向数据库添加一个名为Products_SelectByCategoryID的新存储过程,该存储过程接受一个输入参数(@CategoryID, 类型为int)并将与CategoryID值匹配的所有产品返回.

  执行该CREATE PROCEDURE脚本,将向数据库添加存储过程,点工具栏的保存按钮或按Ctrl+S。如此之后,刷新存储过程文件夹以显示最近添加的存储过程,如此一ilai部分由“CREATE PROCEDURE dbo.Products_SelectProductByCategoryID” 转变为“ALTER PROCEDURE dbo.Products_SelectProductByCategoryID”. CREATE PROCEDURE用于添加新存储过程,而ALTER PROCEDURE用于更新现有的存储过程。由于脚本开头部分已经转变为ALTER PROCEDURE, 我们可以通过改动输入参数或SQL statements并点击保存按钮,即可完成对存储过程的更新.图2显示的是保存Products_SelectByCategoryID存储过程后的画面.

//img.jbzj.com/file_images/article/201605/201605190901552.png
图2:Products_SelectByCategoryID存储过程已经添加到数据库

第二步:设置TableAdapter使用现有的存储过程

  现在存储过程Products_SelectByCategoryID已经添加到数据库,我们将设置数据访问层使用该存储过程。具体说,我们将向ProductsTableAdapter添加GetProducstByCategoryID(categoryID)方法, 该方法将调用我们刚刚创建的存储过程Products_SelectByCategoryID.

  打开NorthwindWithSprocs数据集,在ProductsTableAdapter上右键单击,选“添加查询”以启用TableAdapter Query Configuration wizard.我们将使用刚刚创建的存储过程Products_SelectByCategoryID,因此选“Use existing stored procedure”项,然后点Next.

//img.jbzj.com/file_images/article/201605/201605190901553.png
图3:选“Use existing stored procedure”项

  接下来的画面为一个下拉列表框,列出了数据库现有的所有存储过程,当选择某个存储过程的话,左边将列出其输入参数,右边将列出其返回列(如果有的话).在下拉列表里选Products_SelectByCategoryID存储过程,再点Next.

//img.jbzj.com/file_images/article/201605/201605190901554.png
图4:选Products_SelectByCategoryID存储过程.

  接下来的画面询问我们存储过程返回的是哪种类型的数据,以及TableAdapter的方法返回的类型.比如,如果我们指定返回tabular data(表列数据)的话,该方法将返回一个ProductsDataTable instance实例;如果我们指定存储过程返回一个单一值(a single value)的话,TableAdapter将返回一个object(对象),该对象由存储过程返回的第一行的第一列来赋值.由于存储过程Products_SelectByCategoryID将返回某个category的所有产品,选第一项“Tabular data”,再点Next.

//img.jbzj.com/file_images/article/201605/201605190901555.png
图5:指定存储过程返回Tabular Data

  然后需要指定采用的方法模式以及方法的名称.同时选中Fill a DataTable” 和 “Return a DataTable”项.将这2个方法重命名为FillByCategoryID和 GetProductsByCategoryID. 点Next,确认无误的话,再点Finish完成设置。

//img.jbzj.com/file_images/article/201605/201605190901556.png
图6:将方法命名为FillByCategoryID 和 GetProductsByCategoryID

  注意:我们刚才添加FillByCategoryID 和 GetProductsByCategoryID方法,执行一个int类型的输入参数,它由@CategoryID传递进来。如果你要改动Products_SelectByCategory存储过程的参数的话,你也必须更新这些TableAdapter方法的参数.就像在前一篇文章探讨的一样,要么手动添加或删除参数集里的参数,要么再次运行TableAdapter向导.

第三步:在BLL层添加一个GetProductsByCategoryID(categoryID)方法

设置完DAL层的GetProductsByCategoryID方法后,下一步我们将在业务逻辑层添加方法以调用该方法.打开ProductsBLLWithSprocs class类的文件,添加如下方法:

[System.ComponentModel.DataObjectMethodAttribute
 (System.ComponentModel.DataObjectMethodType.Select, false)]
public NorthwindWithSprocs.ProductsDataTable GetProductByCategoryID(int categoryID)
{
 return Adapter.GetProductsByCategoryID(categoryID);

}

  该BLL层方法仅仅通过ProductsTableAdapter的 GetProductsByCategoryID()方法来返回ProductsDataTable。由于使用了DataObjectMethodAttribute属性,我们使用ObjectDataSource的设置数据源向导时,该方法会出现在SELECT标签的下拉列表中.

第四步:展示产品

  为测试新添加的Products_SelectByCategoryID存储过程,以及DAL 和 BLL层里的对应的方法, 我们将创建一个ASP.NET页面,该页面包含一个DropDownList控件以及一个 GridView控件.DropDownList控件列出数据库里所有的category,当选定某个category时,我们将在GridView里将属于该category的所有product展示出来.

  注意:我们在前面的文章里用DropDownList控件创建过主/从报表,更多细节请参考第7章《使用DropDownList过滤的主/从报表》

  打开AdvancedDAL文件夹里的ExistingSprocs.aspx页面,从工具箱里拖一个DropDownList控件到页面,设置其ID为Categories,AutoPostBack属性为true.接下来,在其智能标签里将其绑定到一个名为CategoriesDataSource的ObjectDataSource控件.设置该控件调用CategoriesBLL class类的GetCategories方法,而在UPDATE, INSERT, 以及DELETE标签里选“(None)”.

//img.jbzj.com/file_images/article/201605/201605190901567.png
图7:调用CategoriesBLL Class类的GetCategories方法

//img.jbzj.com/file_images/article/201605/201605190901568.png
图8:在UPDATE, INSERT,和DELETE标签里选“(None)”

完成ObjectDataSource向导后,我们设置DropDownList控件显示的是CategoryName列,而传递的Value值为CategoryID列.此时,DropDownList控件和ObjectDataSource控件的声明代码看起来和下面的差不多:






  接下来,在DropDownList控件下面放一个GridView控件,社其ID为ProductsByCategory ,并将其绑定到一个名为ProductsByCategoryDataSource的ObjectDataSource控件,该控件调用ProductsBLLWithSprocs class类的GetProductsByCategoryID(categoryID)方法。由于该GridView控件仅仅用来展示数据,因此,在UPDATE, INSERT, 和DELETE标签里选“(None)”并点Next.

//img.jbzj.com/file_images/article/201605/201605190901569.png
图9:设置ObjectDataSource控件ProductsBLLWithSprocs Class类

//img.jbzj.com/file_images/article/201605/2016051909015610.png
图10:调用GetProductsByCategoryID(categoryID)方法

接下来要选择参数来源,我们在Parameter source下拉列表里选“Control”;在ControlID下拉列表里选“Categories” 。点Finish完成设置.

//img.jbzj.com/file_images/article/201605/2016051909015611.png
图11:设参数categoryID来源于ID为Categories的DropDownList控件

完成ObjectDataSource向导后,Visual Studio会自动的添加BoundFields列和一个 CheckBoxField列。你可以对其外观尽情定制.

在浏览器里登录该页面,当登录时选取的是Beverages(饮料类),该类的产品将会显示出来.如果我们选择其它种类的话,对应的所有产品将显示出来.如下图: 

//img.jbzj.com/file_images/article/201605/2016051909015612.png
图12: Produce类的所有产品都显示出来了

第五步:用事务封装存储过程命令

在第61章《在事务里对数据库修改进行封装》里我们探讨了用事务对数据库修改命令进行封装的技术,这些修改操作要么都成功要么都失败。使用事务的技术包括:

.使用System.Transactions命名空间里的类
.在Data Access Layer层调用ADO.NET classes类,比如SqlTransaction
.直接在存储过程里添加T-SQLtransaction commands事务命令

在63章我们在DAL层使用ADO.NET classe类,而在本文剩余部分,我们将在一个存储过程里运用T-SQL command命令来对一个事务进行管理.

用来手动启动、提交、回滚事务的3个主要SQL command命令分别是BEGIN TRANSACTION, COMMIT TRANSACTION, 以及ROLLBACK TRANSACTION.与使用ADO.NET方法类似,在一个存储过程里使用事务时,应采用如下的模式:

1.指出事务已经开启
2.执行事务包含的SQL statements
3.如果第二步的任何一个statement出错,则回滚事务
4.如果第二步的所有statement执行无误,则提交事务

可以用T-SQL syntax来执行该模式,如下:

BEGIN TRY
 BEGIN TRANSACTION -- Start the transaction

 ... Perform the SQL statements that makeup the transaction ...

 -- If we reach here, success!
 COMMIT TRANSACTION
END TRY
BEGIN CATCH
 -- Whoops, there was an error
 ROLLBACK TRANSACTION

 -- Raise an error with the
 -- details of the exception 
 DECLARE @ErrMsg nvarchar(4000),
  @ErrSeverity int

 SELECT @ErrMsg = ERROR_MESSAGE(),
  @ErrSeverity = ERROR_SEVERITY()
 
 RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH

  代码开始为一个TRY...CATCH模式——SQL Server2005新增的结构.就像C#里的try...catch模式一样,该SQL TRY...CATCH模式在TRY区域执行statement,如果任何一个statement出错,则立即转到CATCH区域.

  如果执行无误,COMMIT TRANSACTION将提交更改并完成事务;如果执行出错,那么CATCH区域的ROLLBACK TRANSACTION将数据库返回到开始前的状态。存储过程也会通过RAISERROR command命令抛出一个SqlException异常.
注意:上面代码的的TRY...CATCH模式是SQL Server 2005里新添加的,如果你使用的是Microsoft SQL Server稍微旧点的版本的话,上面的代码不会成功执行。不过你可以参考这篇文章《Managing Transactions in SQL Server Stored Procedures》(http://www.4guysfromrolla.com/webtech/080305-1.shtml)以寻求帮助.

  让我们看一个实实在在的例子。在Categories表和Products表之间有一个外键约束,这意味着,Products表里的CategoryID列必须要与Categories表里的CategoryID值吻合.如果某个category有对应的product,而我们试图删除该category时将会导致违背外键约束.我们来进行演示,登录这个页面(~/BinaryData/UpdatingAndDeleting.aspx),该页面列出了系统里的所有category,且每行都包含Edit和Delete按钮(如图13),如果你尝试删除一个有对应product的category时,比如Beverages——删除失败,因为违背了外键约束(如图14所示).

//img.jbzj.com/file_images/article/201605/2016051909015613.png
图13:每条Category记录都包含Edit 和 Delete按钮

//img.jbzj.com/file_images/article/201605/2016051909015714.png
图14:你无法删除有对应产品的Category

  我们希望可以删除任何一个category,不管其是否有对应的产品.当删除category时,我们同样希望删除其对应的产品(尽管我们可以简单的将这些产品的CategoryID值设置为NULL).为此,我们可以创建一个存储过程,它接受一个输入参数@CategoryID。当调用它时明确的将所有对应的product删除,然后再将这个category删掉.

人们的第一反应是创建类似下面的存储过程:

CREATE PROCEDURE dbo.Categories_Delete
(
 @CategoryID int
)
AS

-- First, delete the associated products...
DELETE FROM Products
WHERE CategoryID = @CategoryID

-- Now delete the category
DELETE FROM Categories
WHERE CategoryID = @CategoryID

  上述代码明白无误的将相关的product以及该category删除,只是没有置身于一个事务内.假设还有其它的基于Categorie表CategoryID值的外键约束,那么在这种情况下问题就出来了:对该category来说,其相关的product都删除掉了,而这个category因与其它表还有外键约束而仍然保留在数据库.

  如果该存储过程置身于一个事务里的话,对Categories表的删除操作失败将导致对Products表的删除操作回滚.下面的存储过程脚本使用一个事务来确保对这2个DELETE statement的原子操作:

CREATE PROCEDURE dbo.Categories_Delete
(
 @CategoryID int
)
AS

BEGIN TRY
 BEGIN TRANSACTION -- Start the transaction

 -- First, delete the associated products...
 DELETE FROM Products
 WHERE CategoryID = @CategoryID


 -- Now delete the category
 DELETE FROM Categories
 WHERE CategoryID = @CategoryID

 -- If we reach here, success!
 COMMIT TRANSACTION
END TRY
BEGIN CATCH
 -- Whoops, there was an error
 ROLLBACK TRANSACTION

 -- Raise an error with the
 -- details of the exception 
 DECLARE @ErrMsg nvarchar(4000),
  @ErrSeverity int

 SELECT @ErrMsg = ERROR_MESSAGE(),
  @ErrSeverity = ERROR_SEVERITY()
 
 RAISERROR(@ErrMsg, @ErrSeverity, 1)
END CATCH

花点时间向Northwind数据库添加一个名为Categories_Delete的存储过程,具体步骤可参考第一步.

第六步:更新CategoriesTableAdapter

  一旦我们添加完Categories_Delete存储过程后,DAL层就可以使用ad-hoc SQL statements来执行删除操作了.不过我们需要更新CategoriesTableAdapter,使其使用Categories_Delete存储过程.

  注意:在前几章我们处理的是NorthwindWithSprocs数据集,该数据集只有一个实体——ProductsDataTable,但是我们将会遇到处理categories的情况。因此,在本文后面部分,当我提到数据访问层(Data Access Layer)时,我指的是Northwind数据集,也就是我们在第1章《创建一个数据访问层》里创建的那个.

  打开Northwind数据集,选中CategoriesTableAdapter并打开其属性窗口,该窗口列出了该TableAdapter用到的InsertCommand, UpdateCommand, DeleteCommand, 以及SelectCommand,以及name和数据库连接信息.展开DeleteCommand属性查看其细节.如图15所示,DeleteCommand的ComamndType属性被设置为Text, 其文本信息作为一个ad-hoc SQL查询.

//img.jbzj.com/file_images/article/201605/2016051909015715.png
图15:在CategoriesTableAdapter的属性窗口查看其属性信息

  让我们来作一些修改.选中“(DeleteCommand)”文本,然后在下拉列表里选“(New)”,这将清除掉CommandText, CommandType,Parameters属性的设置。接着将CommandType属性设置为StoredProcedure,然后在CommandText属性里输入存储过程的名称(即dbo.Categories_Delete).如果你是按照先设置CommandType属性再设置CommandText属性的顺序的话,Visual Studio将自动生成Parameters collection(参数集).如果你没有按照这个顺序来的话,你就只能点击Parameters属性里的一个椭圆型的区域来打开Parameters Collection Editor对话框,手动添加参数.不管是自动的还是手动添加参数,我们都应该打开Parameters Collection Editor对话框以检查参数是否正确(如图16).如果你在对话框里没看到任何的参数,那么就手动添加参数@CategoryID,(你不需要添加参数@RETURN_VALUE).

//img.jbzj.com/file_images/article/201605/2016051909015716.png
图16:确保参数设置正确

  当DAL完成更新后,删除一个category将自动的删除所有其对应的product,这些操作都置身于一个事务里.我们来做个验证,重返刚才那个页面,当单击某个category的Delete按钮时,该category及其所有的product都会被删除.

  注意:在测试Categories_Delete存储过程前,最好对数据库做个备份,因为该存储过程将删除选中的category及其对应的product.如果你用的是App_Data文件夹里的NORTHWND.MDF数据库的话,你只需要关闭Visual Studio并将文件夹里的MDF和LDF文件拷贝到其它文件夹.测试完毕后,关闭Visual Studio,再用备份的MDF和LDF文件覆盖掉App_Data文件夹的对应文件.

结语:

  虽然TableAdapter向导可以自动的生成存储过程,但是在某些时候我们需要使用现有的存储过程。在本文,我们考察了如何在Visual Studio环境里手动添加存储过程,并引导TableAdapter的方法使用这些存储过程。另外我们还考察了在存储过程里用来开启、提交、回滚事务的T-SQL commands 和script脚本模式.

  祝编程快乐!

作者简介

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


推荐阅读
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • switch语句的一些用法及注意事项
    本文介绍了使用switch语句时的一些用法和注意事项,包括如何实现"fall through"、default语句的作用、在case语句中定义变量时可能出现的问题以及解决方法。同时也提到了C#严格控制switch分支不允许贯穿的规定。通过本文的介绍,读者可以更好地理解和使用switch语句。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
author-avatar
finessi_739
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有