失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > ADO.NET使用经验集

ADO.NET使用经验集

时间:2019-01-02 00:49:22

相关推荐

ADO.NET使用经验集

介绍

本文为应用程序提供实现最佳性能、可伸缩性和功能的解决方案;它介绍了中可用对象的使用,并为优化应用程序的设计提供了一些建议。

本文包括:

l .NET框架组件中包含的数据提供者信息

l DataSetDataReader的比较,这些对象最佳使用说明

l 说明怎样使用DataSet、Commands和Connections

lXML集成的信息

l 一般的技巧

.NET框架组件数据提供程序

.NET框架组件中的数据提供程序是应用程序数据源之间的一座桥梁。它允许你从数据源返回查询的结果,在数据源上执行命令,把数据集中的改变提交到数据源。本文包含了怎样选择最适合需求的.NET框架组件数据提供程序。

使用哪种.NET框架组件数据提供程序

为了使应用程序获得最佳的性能,需要使用最适合数据源的.NET框架组件数据提供程序。

连接到SQLServer 7.0及以上版本

当连接到SQLServer 7.0及以上版本时,为了获得最佳性能应该使用SQLServer .NET 数据提供程序。SQLServer .NET数据提供程序设计为直接访问SQLServer,没有其它附加的技术层。下图(图1)说明了访问SQLServer 7.0及以上版本的多种技术之间的差别。

图1.访问SQLServer 7.0及以上版本的连接方法

连接到ODBC数据源

名字空间中的ODBC .NET数据提供程序的结构SQLServer和OLE DB的.NET数据提供程序相同。ODBC .NET数据提供程序使用"ODBC"前缀和标准的ODBC连接字符串。

注意:ODBC .NET数据提供程序包含在.NET框架组件1.1以上版本,包含ODBC .NET数据提供程序的名字空间是System.Data.Odbc。

使用DataReader、DataSet、DataAdapter和DataView

提供两个对象用于检索关系型数据并把它存储在内存中,分别是DataSet和DataReader。DataSet提供内存中关系数据的表现--包括表和次序、约束等表间的关系的完整数据集合。DataReader提供快速、只向前、只读的来自数据库的数据流。

使用DataSet时,一般使用DataAdapter(也可能是CommandBuilder)数据源交互,用DataView对DataSet中的数据进行排序和过滤。DataSet可以被继承来建立强化类型的DataSet,用于暴露表、行、列作为强化类型对象属性。

下面的内容包含什么时候使用DataSet或DataReader,以及怎样优化访问它们所包含的数据,也包括怎样优化DataAdapter和DataView的使用(也包括CommandBuilder)。

DataSetDataReader的对比

在设计应用程序时,决定使用DataSet还是DataReader需要考虑应用程序需要的功能。

使用DataSet是为了实现应用程序的下述功能:

l 操作结果中的多个分离的表。

l 操作来自多个源(例如来自多个数据库、XML文件和电子表格的混合数据)的数据。

l 在层之间交换数据或使用XML Web服务。DataReader 不同,DataSet能被传递到远程客户端。

l 通过缓冲重复使用相同的行集合以提高性能(例如排序、搜索或过滤数据)。

l 每行执行大量的处理。在使用DataReader返回的行上进行扩展处理将使连接存在的时间比必要的更长,从而降低效率。

l 使用XML操作(例如XSLT转换和Xpath查询)维护数据。

在应用程序需要以下功能时使用DataReader:

l 不需要缓冲数据。

l 正在处理的结果集太大而不能全部放入内存中。

l 需要迅速一次性访问数据,采用只向前的只读的方式。

注意:当填充DataSet的时候,DataAdapter使用DataReader。因此使用DataAdapter代替DataSet获得的性能是节约了DataSet消耗的内存和组装DataSet所需要的周期。这种性能的提高大部分是有名无实的,因此你应该根据需要的功能为基础来做设计决定。

使用强类型DataSet的好处

使用DataSet的另一个好处是它能被继承用于建立强类型的DataSet。强类型DataSet的好处包括设计时的检查和强类型DataSet 的Visual Studio .NET语句填充。当你为DataSet固定了大纲或关系结构时,就能建立强类型DataSet,把行和列作为对象的属性而不是项的集合。例如,作为暴露顾客表的某一行的列名的代替,你可以暴露Customer对象的 Name属性。强类型的DataSet衍生自DataSet类,因此不会牺牲DataSet的任何功能,也就是说,强类型的DataSet也可以是远程的,并作为数据绑定控件(例如DataGrid)的数据源提供。如果不知道大纲,也能通过使用通常的DataSet获得好处,但是丧失了强类型DataSet的附加特性。

在强类型DataSet中处理空值

使用强类型DataSet时,你能给DataSet 的XML大纲定义语言(XSD)作注解以确保强类型DataSet正确的处理空(Null)的引用。空值(nullValue)注释使你能用String.Empty这个特定值代替DBNull、保持了空引用、或者产生一个异常。选择其中的哪个依赖于应用程序的内容,默认情况下遇到空引用将产生一个异常。

刷新DataSet中的数据

如果你希望使用更新后的值从服务器刷新数据集中的值,使用DataAdapter.Fill。如果主键定义在数据表上,DataAdapter.Fill基于主键匹配新行,并把服务器的数据改成已存在的行。被刷新行的RowState设置为Unchanged,即使在刷新前它被修改过。注意如果给数据表定义了主键,DataAdapter.Fill添加新行可能重复主键值。

如果希望用服务器的当前值刷新一个表,并且保持表中行的改变,你必须首选使用DataAdapter.Fill组合它,填充一个新的数据表,接着将该数据表合并(Merge)进一个数据集,并把preserveChanges值设为true。

在DataSet中搜索数据

一个数据集中查询符合特定条件的行时,使用基于索引(index-based)的查看表将提高性能。给数据表指定主键(PrimaryKey)值时,就建立了一个索引。当为数据表建立数据视图(DataView)时也建立了索引。下面是一些使用基于索引查看的技巧:

如果查询是在数据表的主键列上进行的,使用DataTable.Rows.Find代替DataTable.Select。

查询非主键列,可以使用数据视图来提高多个数据查询的速度。当给数据视图添加排序时,将建立搜索时使用的索引。数据视图暴露了查询下层数据表的Find和FindRows方法。

如果你不是查询表的排序视图,也可以通过为数据表建立数据视图获得基于索引的查看表的好处。注意如果你执行数据上的多个查询这是唯一的好处。如果你只执行单个查询,需要建立索引的过程将因为使用索引而降低了性能。

数据视图(DataView)结构

当数据视图建立后,并且当Sort、RowFilter或RowStateFilter或者属性被修改时,数据视图为下层数据表中的数据建立索引。当建立数据视图对象时,使用把Sort、RowFilter和RowStateFilter值作为参数的数据视图构造函数。结果是建立了一次索引。建立"空"数据视图,然后设置Sort、RowFilter和RowStateFilter属性将导致至少两次建立索引。

分页

给了你从数据源返回什么数据的明显控制,也提供了在数据集中存储了多少数据的控制。在设计应用程序时可以考虑以下技巧:

l 避免使用DataAdapter.Fill,它使用了startRecord和maxRecords值。使用这种方式填充数据集时,数据集只填充由maxRecords参数指定的记录个数(从参数startRecord指定的记录开始),而不管返回的整个查询。这导致读取过时的"不想要的"记录,同时使用了不必要的服务器资源来返回补充记录。

l 用于在某个时候只返回一页记录的技术之一是建立一个SQL语句,该语句包含一个WHERE和ORDER BY子句,并有TOP判定。这种技术依赖于识别每个唯一行的方法。当导航到下一页的记录时,修改WHERE子句使它包含所有唯一标识比当前页标识大的记录;当导航到前面一页时,修改WHERE子句使它包含所有唯一标识比当前页标识小的记录。对于两种查询都只返回记录的TOP页的记录。当导航到前面一页时需要对记录进行降序排列,这将返回查询的末尾页(如果需要可以在显示前对记录进行重新排序)。

l 另一种技术是建立一个SQL语句包含TOP判定和嵌入的SELECT语句。这种技术不是基于唯一的识别每行的方法。使用这种技术的第一步是把页面的大小想得到的页面数量相乘。接着把该数值传递给SQL查询的TOP判定,并按升序排序。接着把这个查询嵌入另一个查询,该查询从嵌入的查询结果中选择TOP页面大小,按降序排列。本质上返回的是嵌入的查询的末尾页面。例如,为了返回页面大小是10的查询结果的第三页,使用下面的命令:

l 如果数据不是经常改变,能通过本地维护数据集里面的记录缓存来提高性能。例如,你能在本地数据集中存储10页数据,只在用户导航超出第一页或最后一页时才查询数据源检索新的数据。

使用大纲(Schema)填充数据集

当用数据填充数据集时,DataAdapter.Fill方法使用数据集的已存在的大纲并把它Select命令(SelectCommand)返回的数据进行组合。如果数据集中没有被填充的表匹配的表的名字,Fill方法将建立一张表。默认情况下,Fill只定义列和列的类型。

你能通过设置数据适配器的MissingSchemaAction属性来重载Fill的默认的行为。例如,要使Fill建立的表包含主键信息、唯一约束、列属性、是否允许空值、列的最大长度、只读列、自动增加列等等,只需要指定DataAdapter.MissingSchemaAction为MissingSchemaAction.AddWithKey。作为选择,你能在调用DataAdapter.Fill前调用DataAdapter.FillSchema来确保数据集被填充时大纲已经准备好了。

调用FillSchema将再次访问服务器并检索附加的大纲信息。为了提高性能,最好指定数据集的大纲,或者在调用Fill前设置数据适配器的MissingSchemaAction。

使用命令构造器(CommandBuilder)的经验

命令构造器根据数据适配器的SelectCommand属性自动生成数据适配器的InsertCommand、UpdateCommand和DeleteCommand属性(假若SelectCommand执行单个表上的选择(SELECT))。

l 命令构造器的使用应该限制在设计时或者ad-hoc情况下。需要的生成数据适配器命令属性的过程妨碍了性能。如果你预先知道INSERT/UPDATE/DELETE语句的内容,应该显式地设置它们。好的设计技巧是为INSERT/UPDATE/DELETE命令建立存储过程并明确地配置数据适配器命令属性来使用它们。

l 命令构造器使用数据适配器的SelectCommand属性来决定其它命令属性的值。如果数据适配器的SelectCommand自身改变了,一定要调用RefreshSchema来更新命令属性。

l 如果命令属性是空的(默认情况下命令属性是空的),命令构造器只为数据适配器命令属性生成一个命令。如果你明确地设置一个命令属性,命令构造器不会覆盖它。如果你希望命令构造器为一个已经设置了的命令属性生成一个命令,要把命令属性设置为空。

批处理SQL语句

很多数据库支持在一个命令执行中组合、批处理多个命令执行。例如,SQLServer允许你使用分号分隔命令。把多个命令组合成为一个减少了对服务器的访问次数,可以提高应用程序的性能。例如,你能在本地应用程序中存储所有的删除,并在数据源发布一个批处理命令调用来删除它们。

尽管它提高了性能,但是也增加了应用程序管理数据集里面数据更新的复杂性。为了保持简单性,你也许会为数据集中的每个数据表建立一个数据适配器。

使用多个表填充数据集

如果使用批处理SQL语句检索多个表并填充一个数据集,第一张表的名字使用Fill方法指定的表名,后面的表的名字是Fill方法指定的名字加上一个数字,从1开始逐渐增加。例如,如果运行下面的代码:

从Customers表中得到的数据放在叫"Customers"的数据表中,从Orders表中得到的数据放在"Customers1"数据表中。

你可以在数据表被填充后修改"Customers1"表的属性为"Orders"。但是接下来的填充的结果是"Customers"表被重新填充,但是"Orders"表被略过了并且建立了另一个"Customers1"表。为了避免这种情况,建立一个把"Customers1"映射到"Orders"的DataTableMapping,并且为其它的表建立映射。例如:

使用DataReader

下面是使用DataReader提高性能的一些技巧:

l 在访问任何命令(Command相关的输出参数前DataReader必须关闭。

l 在读完数据后就关闭DataReader。如果你正在使用的连接只返回该DataReader,在关闭DataReader后立即关闭连接。

l 另一种明确地关闭连接的方法是给ExecuteReader方法传递CommandBehavior.CloseConnection以确保当DataReader关闭时相关的连接关闭了。如果你从某个方法返回DataReader,并且没有办法控制DataReader或者相关的连接关闭的情况下特别有用。

l DataReader不能在层之间远程访问。DataReader是设计用于连接数据访问的。

l 使用类型化的存取程序(例如GetString、GetInt32等等)来访问列数据。这节省了将GetValue返回的对象作为特定类型的必要的处理。

l 在某一时刻只有一个DataReader能够打开。。在ADO中,如果你打开一个连接并请求两个使用只向前的只读游标的记录集,ADO隐性地为游标的生命周期的数据存储打开第二个不在连接池中的连接,接着隐性地关闭它。在中,如果你想在同一个数据存储上同时打开两个DataReader,你必须明确地建立两个连接,每个DataReader一个。通过这种方法给了你对连接池使用的更多控制。

l 默认情况下,DataReader在每个Read方法中把整个行载入内存中。这允许你随机访问当前行的任意列。如果随机访问是不必要的,为了提高性能,把CommandBehavior.SequentialAccess传递给ExecuteReader调用。这改变了DataReader的默认行为,只在需要时才把数据载入内存。注意CommandBehavior.SequentialAccess要求你按次序访问返回的列。也就是,一旦你读过了返回的某个列,就不能再次读取它的值了。

l 如果你结束了从DataReader中读取数据,但是仍然有大量的未读取的结果等待,那么调用Command的Cancel比调用DataReader 的Close好。调用DataReader 的Close引起它检索等待的结果并且先清空流后关闭游标。调用Command的 Cancel删除服务器上的结果,因此当DataReader关闭时,它不需要再读取结果。如果你从Command返回输出参数,则调用Cancel删除它们。如果你要读取任何输出参数,不要调用Command的Cancel;最后调用DataReader的 Close。

二进制大对象(BLOB)

当使用DataReader检索二进制大对象时,必须给ExecuteReader方法调用传递CommandBehavior.SequentialAccess。因为DataReader的默认行为是在每个Read中把整行载入内存中,但是由于BLOB可能很大,结果可能是一个BLOB对象使用大量的内存。SequentialAccess把DataReader的行为设置为只载入必要的数据,接着你能使用GetBytes或者GetChars控制每次载入多少数据。

记住使用SequentialAccess时,你不能无序地访问DataReader返回的不同字段。也就是说,如果查询返回三个列,第三个是BLOB,并且你希望访问前两个列的数据,你必须先访问第一个列,接着在访问BLOB数据前访问第二个列。这是因为现在数据是按次序返回的,在DataReader读过它后不能再次访问。

使用命令(Command

为命令执行提供了几个不同的方法,同时也为优化命令的执行提供了不同的选择。下面的技巧包括怎样选择最好的命令执行和怎样提供一个被执行命令的性能。

使用OleDbCommand的最好经验

不同的.NET框架组件数据提供程序之间的命令执行是尽可能标准的。但是,在这些数据提供程序间也有些不同。下面的一些可以调整OLE DB数据提供程序命令执行的技巧:

lODBC CALL语法一起使用CommandType.Text来调用存储过程。仅仅使用CommandType.StoredProcedure生成ODBC CALL语法。

l 一定要设置OleDbParameter类型、大小(如果可用)、精度和小数位(如果参数是数值型或者十进制型)。注意如果你没有明确地提供参数信息,OleDbCommand使用每一个命令执行重新建立OLE DB参数存取程序。

使用SqlCommand的经验

使用SqlCommand执行存储过程的技巧:如果你要调用一个存储过程,为SqlCommand的 CommandType属性指定StoredProcedure的CommandType。这样就删除了在命令执行前分析命令的需求,明确地把它标识为存储过程了。

Prepare方法的使用

Command.Prepare方法能够提高数据源中重复的参数化命令的性能。Prepare指示数据源为多个调用优化特定的命令。为了更高效率地使用Prepare,你必须十分清楚数据源怎样回应Prepare调用。对于类似SQLServer 2000的数据源,命令是隐式优化的,对Prepare的调用是没有必要的,但是对于另一些数据源,例如SQLServer 7.0,Prepare效率更高。

明确地指定大纲和元数据

在中当用户没有指定元数据信息时,有很多对象推导这些信息。例如:

l DataAdapter.Fill方法,如果不存在的话,它在记录集中建立表和列。

l CommandBuilder,它为单个的SELECT语句生成数据适配器命令属性。

l CommandBuilder.DeriveParameters,它组合Command对象的Parameters集合。

但是每次使用这些特性时都会造成效率降低。我们推荐主要在设计时和ad-hoc应用程序中使用这些特性。在可能的情况下,明确地指定大纲和元数据,包括在数据集中定义表和列,定义数据适配器的Command属性,定义Command的Parameter信息。

ExecuteScalar和ExecuteNonQuery

如果你希望返回单个值,例如Count(*)、 Sum(Price)、或者Avg(Quantity),你可以使用Command.ExecuteScalar。ExecuteScalar返回第一行第一列的值,返回结果集是数量值。ExecuteScalar通过一步完成不仅简化了代码而且提高了性能,而这些工作在使用DataReader时将需要两个处理步骤。

当使用不返回行的SQL语句时,类似修改数据(例如插入、更新或者删除)或者只返回输出参数或值,使用ExecuteNonQuery。它通过建立一个空DataReader删除了任何必要的处理。

空值的检测

如果数据库的某张表的一个列允许空值,你不能使用某个空值相等的参数来测试它。作为代替,需要编写一个WHERE子句来检测是否列和参数都是空值。下面的SQL语句返回LastName列赋予@LastName的值相同的行,或者LastName 列和@LastName参数都为空的行:

把空(Null)作为参数值传递

当在命令中把空值作为参数值发送给数据库时,不能使用null(Visual Basic .NET中的Nothing)。作为代替必须使用DBNull.Value。例如:

执行事务

事务模块为作了一些改变。在ADO中,当调用StartTransaction时,该调用后面的任何更新都被认为是该事务的一部分。但是在中,当调用Connection.BeginTransaction时,返回的Transaction对象必须Command的Transaction属性关联。这种设计使你能在一个连接上执行多重事务。如果Command.Transaction属性没有设置为开始就连接关联的Transaction,Command失败并出现异常。

使用连接

高性能的应用程序保持使用最少次数的数据源的连接,也利用了类似连接池的性能增强技术。下面的技巧帮你使用连接数据源时获得更好的性能。

连接池

SQLServer、OLE DB和.NET框架组件数据提供程序隐性为ODBC提供了连接池。你可以在连接字符串中指定不同的属性控制连接池的行为。

用DataAdapter优化连接

数据适配器的Fill和Update方法自动地为相关的命令属性打开特定的连接(如果它被关闭的话)。如果Fill或Update方法打开了连接,Fill或Update将在操作完成时关闭它。为了提高性能,只在必要时保持数据库连接打开,同时为多个操作减少打开和关闭连接的次数。

我们推荐如果你只执行单个的Fill或Update方法调用,你应该允许Fill或Update隐式打开和关闭连接。如果大量调用Fill或者Update,我们推荐显式打开,进行Fill或Update调用,然后显式关闭连接。

此外执行事务时,在开始事务前明确地打开连接,在完成事务后明确地关闭连接。例如:

经常关闭连接(Connection)和DataReader

当停止使用Connection或者DataReader对象时,明确地关闭它们。尽管无用单元收集程序最终会清除这些对象,并释放连接和其它可管理资源,但是无用单元收集只在必要时才发生。因此确保昂贵的资源明确地被释放仍然是你的职责。此外,连接如果没有被明确的释放将使它不会返回连接池。例如,如果连接池到达了最大值并且一个连接还有效,该超出范围并且没有被明确关闭的连接才返回到连接池。

注意不要在类的Finalize方法中调用Connection、DataReader、或者其它可管理对象的Close或者Dispose方法。在该方法中只释放类直接拥有的不可管理资源。如果类中没有任何不可管理资源,在类定义中不要包含Finalize方法。

在C#中使用"Using"语句

对C#程序员来说,确保经常关闭Connection和DataReader对象的一个简便方法是使用using语句。Using语句会自动调用留在Using语句范围内的被使用的对象上的Dispose,如下所示:

避免访问OleDbConnection.State属性

如果连接打开了,OleDbConnection.State使本地OLE DB向DATASOURCEINFO属性集调用IDBProperties.GetProperties来获取DBPROP_CONNECTIONSTATUS属性,这可能引起重新返回数据源。换句话说,检查State属性可能花费很大。因此只在必要时才检查State属性。如果你需要经常检查该属性,你监听OleDbConnection的StateChange事件会使应用程序的性能更好。

XML集成

在数据集中提供了广泛的XML集成,并且暴露了一些SQLServer 2000及以上版本所提供的XML功能。你能使用SQLXML 3.0来访问SQLServer 2000及以上版本所提供的XML功能。下面是使用XML和的一些技巧和信息。

数据集XML

数据集XML紧密结合,提供了执行下面操作的能力:

l 从XSD大纲载入数据集的大纲或者关系结构。

l 从XML载入数据集的内容。

l 当没有提供大纲时根据XML文档的内容推断数据集的大纲。

l 将数据集的大纲写成XSD大纲。

l 将数据集的内容写成XML。

l 使用数据集同步访问数据的相关表现、使用XmlDataDocument访问数据的层次表现。

注意:你能使用这种同步在数据集的数据上应用XML功能(例如Xpath查询和XSLT变换),或提供所有的关系型视图,或者在保持原XML不变的情况下提供XML文档中的数据的子集。

大纲接口

当从XML文件中载入数据集时,你能从XSD大纲中载入数据集的大纲,或者在载入数据前预先定义表和列。如果没有XSD大纲,并且你也不知道为XML文件的内容定义怎样的表和列,你能根据XML文档的结构推断大纲。

大纲推理作为迁移工具是有用的,但是由于推理过程有下面的限制,它只限于应用程序设计时使用:

l 推理大纲引入了附加的处理将降低应用程序的性能。

l 所有推理列的类型都是字符串型。

l 推理过程是不确定的。这就是说,它基于XML文件而不是预定的大纲。结果是你可能有两个XML文件,它们有相同的预定大纲,却因为它们的内容不同形成了两个完全不同的推理大纲。

为XML查询服务的SQLServer

如果你为XML查询返回SQLServer 2000结果,你能使用.NET框架组件SQLServer数据提供程序直接用SqlCommand.ExecuteXmlReader方法建立一个XmlReader。

SQLXML可管理类

在.NET框架组件中有一些类暴露了XML为SQLServer 2000提供的功能。这些类都在Microsoft.Data.SqlXml名字空间中,添加了执行Xpath查询和XML模板文件的能力,也能把XSLT转换为数据。

避免自动增加(Auto-Increment)值冲突

和许多数据源一样,DataSet允许你在添加新行时识别自动增加值的列。在DataSet中使用自动增加列时,由于数据源也有自动增加列,需要避免添加到DataSet中的本地行号添加到数据源中的行之间的冲突。

例如,假设一个表的自动增加主键列是CustomerID。两个新客户信息添加到该表,获得的自动增加CustomerID值分别是1和2。接着只有第二个客户行给数据适配器传递的Update方法,在数据源中新添加的行接受的自动增加CustomerID值是1,数据集中的2不匹配。当数据适配器用返回值填充表中的第二行时,由于第一个顾客行的CustomerID是1,便出现了错误。

为了避免这种情况,我们推荐当使用数据源和数据集中有自动增加列时,数据集中的该列的AutoIncrementStep设为-1,AutoIncrementSeed设为0,同时确保数据源中生成的自动增加标识值从1开始,步长为正。结果是数据集生成负的自动增加值,不会数据源产生的正自动增加值冲突。另一种选择是使用Guid类型的列带有自动增加列,该算法产生的Guid值在数据集和数据源中永远不同。

如果你的自动增加列永远简单的作为唯一值,没有其它的意义,考虑使用Guid代替自动增加列。它们是唯一的,避免了做另外的工作处理自动增加列。

查找优化的并发性故障

因为DataSet被设计为从数据源断开,所有必须确保当多个客户端更新数据源的数据时应用程序避免冲突。

测试优化并发性错误有多种技术。一种是在表的列中包含时间戳。另一种技术是通过使用SQL语句中的WHERE条件检测来验证行中所有的源列值数据库中的匹配。

多线程编程

的优化是为了提高性能、吞吐量和可伸缩性。结果是不锁定资源并且只能在单个线程中使用,其中一个例外是DataSet,它对多个阅读程序来说是线程安全安的。但是在写的时候必须锁定DataSet。

只在必要的时候使用COM交互操作(Interop)访问ADO

被设计成大量应用程序的最佳解决方案。但是,有些应用程序需要只能使用ADO对象。在这些情况下,应用程序能使用COM交互操作访问ADO。注意使用COM交互操作访问ADO的数据将极大的降低性能。设计应用程序时,在实现使用COM交互操作访问ADO这种设计前首选决定是否符合设计需要。

避免自动增加(Auto-Increment)值冲突

和许多数据源一样,DataSet允许你在添加新行时识别自动增加值的列。在DataSet中使用自动增加列时,由于数据源也有自动增加列,需要避免添加到DataSet中的本地行号添加到数据源中的行之间的冲突。

例如,假设一个表的自动增加主键列是CustomerID。两个新客户信息添加到该表,获得的自动增加CustomerID值分别是1和2。接着只有第二个客户行给数据适配器传递的Update方法,在数据源中新添加的行接受的自动增加CustomerID值是1,数据集中的2不匹配。当数据适配器用返回值填充表中的第二行时,由于第一个顾客行的CustomerID是1,便出现了错误。

为了避免这种情况,我们推荐当使用数据源和数据集中有自动增加列时,数据集中的该列的AutoIncrementStep设为-1,AutoIncrementSeed设为0,同时确保数据源中生成的自动增加标识值从1开始,步长为正。结果是数据集生成负的自动增加值,不会数据源产生的正自动增加值冲突。另一种选择是使用Guid类型的列带有自动增加列,该算法产生的Guid值在数据集和数据源中永远不同。

如果你的自动增加列永远简单的作为唯一值,没有其它的意义,考虑使用Guid代替自动增加列。它们是唯一的,避免了做另外的工作处理自动增加列。

查找优化的并发性故障

因为DataSet被设计为从数据源断开,所有必须确保当多个客户端更新数据源的数据时应用程序避免冲突。

测试优化并发性错误有多种技术。一种是在表的列中包含时间戳。另一种技术是通过使用SQL语句中的WHERE条件检测来验证行中所有的源列值数据库中的匹配。

多线程编程

的优化是为了提高性能、吞吐量和可伸缩性。结果是不锁定资源并且只能在单个线程中使用,其中一个例外是DataSet,它对多个阅读程序来说是线程安全安的。但是在写的时候必须锁定DataSet。

只在必要的时候使用COM交互操作(Interop)访问ADO

被设计成大量应用程序的最佳解决方案。但是,有些应用程序需要只能使用ADO对象。在这些情况下,应用程序能使用COM交互操作访问ADO。注意使用COM交互操作访问ADO的数据将极大的降低性能。设计应用程序时,在实现使用COM交互操作访问ADO这种设计前首选决定是否符合设计需要。

- 作者: shfwlj 04月19日, 星期二 11:05 回复(0) | 引用(0) 加入博采

中调用存储过程大全
在 Visual Basic .NET 中使用存储过程

简介

本文假设您已经了解了 的基础知识。如果您在工作中从未使用过 中的DataAdapterDataSetCommand对象,则应阅读一些介绍 的文章,包括 Rocky 为本专栏撰写的名为您一文。

简而言之,DataSet在 中用作数据容器,并在数据库断开连接时使用。DataSet包含一个或多个DataTable,每个DataTable都包含行集合。对于那些熟悉传统 ADO 环境的用户来说,DataTable可被看作是断开连接的 Recordset。

DataAdapter在连接到数据库时工作。单个DataAdapter的作用是使用数据库中的数据填充某个DataTable,或将DataTable中的更改写回到数据库,或者二者兼而有之。

DataAdapter要求Command对象执行各种数据库操作。Command对象存放SQL语句或指定数据访问实现方法的存储过程名称。每个DataAdapter有四个属性,指定用于四种数据访问类型之一的命令对象。

SelectCommand:此Command对象用于从数据库中选择数据。UpdateCommand:此Command对象用于更新数据库中的现有记录。InsertCommand:此Command对象用于向数据库中插入新记录。DeleteCommand:此Command对象用于删除数据库中的现有记录。

图 1 阐释了这些对象及其关系。

图 1:用于访问存储过程的主要 类以及它们之间的关系

到目前为止,您所看到的演示软件示例可能将其Command对象配置为使用SQL语句进行数据访问。实际上,某些示例可能完全跳过了Command对象的创建,这是因为DataAdapter的某个构造函数允许Command对象选择后台创建的数据。在使用存储过程之前,让我们运行这样一个示例进行比较。

本文中的所有示例都使用SQLServer 附带的 Northwind 示例数据库。我们还使用专门为SQLServer 创建的 类,而不是普通的 OLE DB 类。为了便于访问这些SQLServer 类,所有示例都需要在应用程序的代码顶部加上以下代码行:

Imports System.Data.SQLClient

现在,让我们看看不使用存储过程执行数据访问的第一个示例。在此示例中,我们将在 Northwind 数据库 Products 表中检索所有产品。创建一个新 Windows 应用程序,在出现的空白 Form1 上,放置一个按钮和一个DataGrid。将DataGridAnchor属性设置为全部四个边,使之随表单的扩展而扩展。在按钮的Click事件中,放置以下代码:

Dim sConnectionString As String = _"server=localhost;uid=sa;pwd=;database=Northwind"Dim sSQL As String = "SELECT * FROM Products"Dim daGetProducts As New SqlDataAdapter(sSQL, sConnectionString)Dim dsProducts As New DataSet()daGetProducts.Fill(dsProducts, "Products")DataGrid1.DataSource = dsProducts.Tables("Products")

根据计算机配置的不同,可能需要更改连接字符串。建立数据库连接后,其余代码应该可以正常运行。此演示软件说明了填入和使用DataSet的最简单方法。

请注意,代码并不创建Connection对象或Command对象。事实上,没有这些对象, 便无法工作,但它们是在后台创建并使用的。实例化SqlDataAdapter的代码行传入SQL字符串(用于配置后台Command对象)和连接字符串(用于配置后台Connection对象)。

我们可以将此代码更改为使用显式ConnectionCommand对象,以便稍稍远离演示软件。在表单上再放置一个按钮,并将以下代码放到Click事件中:

Dim sConnectionString As String = _"server=localhost;uid=sa;pwd=;database=Northwind"Dim sSQL As String = "SELECT * FROM Products"Dim cnNorthwind As New SqlConnection(sConnectionString)Dim cmdProducts As New SqlCommand(sSQL, cnNorthwind)Dim daGetProducts As New SqlDataAdapter(cmdProducts)Dim dsProducts As New DataSet()daGetProducts.Fill(dsProducts, "Products")DataGrid1.DataSource = dsProducts.Tables("Products")

此代码通过显式创建ConnectionCommand对象,并将这些对象附加到DataAdapter,说明了DataAdapters的常用性。通过在实例化DataAdapter时传入 cmdProducts,DataAdapterSelectCommand将自动设置。然后,可以立即使用DataAdapter访问数据库。

此代码的结果前一示例中的结果相同。尽管它有点接近真实软件,但由于数据访问是通过SQL语句实现的,因此仍然属于演示软件。

使用简单存储过程获取数据

如何将此演示软件更改为使用存储过程?只需更改几行代码。在表单上再放置一个按钮,并将以下代码放到Click事件中:

Dim sConnectionString As String = _"server=localhost;uid=sa;pwd=;database=Northwind"Dim cnNorthwind As New SqlConnection(sConnectionString)Dim cmdProducts As New _SqlCommand("十件最贵的产品", cnNorthwind)mandType = CommandType.StoredProcedureDim daGetProducts As New SqlDataAdapter(cmdProducts)Dim dsProducts As New DataSet()daGetProducts.Fill(dsProducts, "Products")DataGrid1.DataSource = dsProducts.Tables("Products")

实例化Command对象时,此代码不使用SQL语句并替换为要使用的存储过程名称。此外,Command对象的CommandType属性必须设置为StoredProcedure

此后的代码一个示例非常相似,但它返回不同的数据。存储过程查找十件最贵的产品,并只返回每个产品的名称和价格。

带输入参数的存储过程

此示例很简单,因为存储过程不需要任何输入参数。也就是说,查找十件最贵的产品不需要任何外部信息。无需外界帮助,存储过程即可完成此操作。然而,多数存储过程都需要输入参数来执行其功能。在下一个示例中,让我们看看如何向存储过程传递输入参数。我们将使用CustomerID来获取相关客户的所有订单,并使用名为CustOrderHist的存储过程(已存在于 Northwind 数据库中)。

在已使用的表单上再创建一个按钮,并将以下代码行放到按钮的Click事件后面:

Dim sConnectionString As String = _"server=localhost;uid=sa;pwd=;database=Northwind"Dim cnNorthwind As New SqlConnection(sConnectionString)Dim cmdOrders As New SqlCommand("CustOrderHist", cnNorthwind)mandType = CommandType.StoredProcedure' 为存储过程设置参数 Dim prmCustomerID As New SqlParameter()prmCustomerID.ParameterName = "@CustomerID"prmCustomerID.SqlDbType = SqlDbType.VarCharprmCustomerID.Size = 5prmCustomerID.Value = "ALFKI"cmdOrders.Parameters.Add(prmCustomerID)Dim daGetOrders As New SqlDataAdapter(cmdOrders)Dim dsOrders As New DataSet()daGetOrders.Fill(dsOrders, "Orders")DataGrid1.DataSource = dsOrders.Tables("Orders")

此代码一个示例中的代码非常相似,不同之处在于创建Command对象之后,为其配置了Parameter对象并将此对象添加到Command的参数集合中。在此示例中(更接近于演示软件)将对客户 ID 进行硬编码,参数的Value属性通常会设置为某些用户输入数据。但是,参数的其他属性可以完全象此示例中那样设置。

此示例中的所有参数设置都是显式设置。某些开发人员喜欢这种样式,因为它便于说明。但某些开发人员喜欢使用代码行较少的等价方法:

Dim sConnectionString As String = _"server=localhost;uid=sa;pwd=;database=Northwind"Dim cnNorthwind As New SqlConnection(sConnectionString)Dim cmdOrders As New SqlCommand("CustOrderHist", cnNorthwind)mandType = CommandType.StoredProcedurecmdOrders.Parameters.Add(New _ SqlParameter("@CustomerID", SqlDbType.VarChar, 5))cmdOrders.Parameters("@CustomerID").Value = "ALFKI"Dim daGetOrders As New SqlDataAdapter(cmdOrders)Dim dsOrders As New DataSet()daGetOrders.Fill(dsOrders, "Orders")DataGrid1.DataSource = dsOrders.Tables("Orders")

此代码上一示例的作用完全相同。但每个参数只需要两行代码,而不是六行。如果存储过程包含大量参数(如后面某些示例所示),所需代码行的多少就会有明显区别,因此在后面部分,我们将使用此表单。

使用存储过程更新数据库

以上示例使用存储过程从数据库中提取信息。在复杂应用程序中使用存储过程更新、插入和删除记录也很常见。让我们看看如何使用 完成该操作。

在第一个示例中,我们将使用 Visual Studio® .NET 中的向导编写一个存储过程集合,并创建使用这些过程的代码。尽管我们只需在此示例中编写最少量的代码,但检查向导创建的代码有助于我们理解除获取数据以外,有关存储过程交互操作的过程。

在此示例中,我们将使用 Northwind 示例数据库中的 Customers 表。安装后的 Northwind 数据库中不包含用于更新、插入或删除客户的存储过程,但 Visual Studio .NET 中的 DataAdapter Configuration Wizard(数据适配器配置向导)可轻松地为我们编写一些存储过程。

启动新的Windows Application(Windows 应用程序)项目。在空白的 Form1 上,放置一个DataGrid和两个按钮。和先前一样,更改DataGridAnchor属性使之锚定到全部四个边。将按钮命名为btnFillbtnUpdate,并分别将其Text属性更改为FillUpdate

转到Toolbox(工具箱)的Data(数据)选项卡,将SqlDataAdapter控件拖动到窗体上,然后释放鼠标。这将启动 DataAdapter Configuration Wizard(数据适配器配置向导)。单击Next(下一步)按钮开始向向导中输入信息。

首先,需要选择一个到 Northwind 数据库的连接;如果列表中未显示所需连接,则单击New Connection(新建连接)按钮创建一个连接。然后单击Next(下一步)按钮。

下一屏幕上将出现三种数据访问方法。其外观图 2 类似。

图 2:选择用于 DataAdapter 的数据访问类型

此时,多数演示软件示例选择第一个选项来使用SQL语句。但是,我们将使用第二个选项,并让向导为我们生成一些存储过程。选择Create new stored procedures(创建新存储过程)选项,然后单击Next(下一步)按钮。

下一屏幕将请求SQL语句,指示最初从数据库中提取的数据。但并不直接使用此SQL语句。SQL语句中的信息将用于构造存储过程,以便执行实际数据访问。为使示例简单起见,请输入SQL语句 SELECT * FROM Customers,然后按Next(下一步)按钮。

此时,向导会请求要创建的存储过程的名称。操作共有四种 - Select、Update、Insert 和 Delete 操作。按以下方法对其命名:

Select:MSDNSelectCustomersUpdate:MSDNUpdateCustomerInsert:MSDNInsertCustomerDelete:MSDNDeleteCustomer

选择Yes, create them in the database for me(是的,在数据库中创建它们。)选项。此时,向导屏幕应该如图 3 所示。

图 3:命名要由 DataAdapter 向导创建的存储过程

单击Next(下一步)按钮。向导将创建存储过程并在状态栏屏幕上指示其进度。完成后,可单击Finish(完成)按钮退出向导。

向导创建了配置完整的DataAdapter,但未创建DataSet来存放数据。这是我们下一步要做的。在Toolbox(工具框)的Data(数据)选项卡中,拖动DataSet控件。出现配置屏幕时,选择Untyped dataset(无类型的数据集)。

现在我们准备使用DataAdapter填充数据集。在btnFillClick事件中,放入以下两行代码:

SqlDataAdapter1.Fill(DataSet1, "Customers")DataGrid1.DataSource = DataSet1.Tables("Customers")

btnUpdateClick事件中,放入以下代码行:

SqlDataAdapter1.Update(DataSet1, "Customers")

现在我们有了一段使用存储过程进行数据访问的有效演示软件。可以运行程序并单击Fill按钮获取网格中的客户列表。然后,可在窗格中编辑客户记录并选择Update按钮将更改放回到数据库中。

注意:编辑第一列(即CustomerID)时将出现异常,因为在SQLServer 中不能更新数据库记录中的主键。

查看由向导生成的代码会有所帮助,所有这些代码最初都隐藏在Windows Form Designer generated code(Windows 窗体设计器生成的代码)区域中。单击该区域的加号可展开代码。注意以下代码,它对所需的SQLDataAdapter和四个命令对象进行了实例化:

Me.SqlDataAdapter1 = New System.Data.SqlClient.SqlDataAdapter()Me.SqlSelectCommand1 = New System.Data.SqlClient.SqlCommand()Me.SqlInsertCommand1 = New System.Data.SqlClient.SqlCommand()Me.SqlUpdateCommand1 = New System.Data.SqlClient.SqlCommand()Me.SqlDeleteCommand1 = New System.Data.SqlClient.SqlCommand()

此后的代码配置每个命令对象并为其创建参数集合。此代码一个示例相似,它们都使用带参数的存储过程。但向导生成的代码使用参数的某些附加属性,以使其更改数据的存储过程协同工作。例如,用于创建SQLInsertCommand1CompanyName参数的代码:

Me.SqlInsertCommand1.Parameters.Add(New _System.Data.SqlClient.SqlParameter("@CompanyName", _System.Data.SqlDbType.NVarChar, 40, "CompanyName"))

在上一个示例中,我们只为参数名称、数据类型和长度等设置了属性。此代码还会将参数的SourceColumn属性设置为值CompanyName。该属性指示 DataSet 的CustomersDataTable此参数对应的字段。这使DataTable中的值在插入操作期间自动插入到参数的Value属性中。让我们来详细介绍一下。

调用SQLDataAdapterUpdate方法时,该方法将更新 DataSet 中的单个DataTable。当逐行检查DataTable时,会查找需要更新、插入或删除的行。当找到需要插入到数据库中的行后,SQLDataAdapter将使用由其InsertCommand属性设置的Command对象。这种情况下,Command对象将访问MSDNInsertCustomer存储过程。

在该存储过程运行前,每个参数的Value属性都必须从插入的行中导入。配置SQLDataAdapter1的代码将存储过程的每个参数DataTable中的相应字段相关联。这样,新 DataTable 行中的数据将自动传输到存储过程的参数。

其他存储过程参数的配置方法此相似。但有一个不同之处值得注意。其他存储过程传入DataTable中数据的原始值,这些值用于检查数据是否在您不知情的情况下发生了更改。也就是说,如果您提取了某些数据,而其他人在您尝试更新前更改了该数据,您将收到并发异常。启动以上程序、提取客户,然后使用工具(例如SQLEnterprise Manager)更改记录中的内容,便可以看到这种情况的发生。如果您在示例程序中更改同一记录并尝试进行更新,则会收到并发异常。

从存储过程返回值

以上示例有一个不足之处。Northwind Customers 表使用数字字母形式的主键,并且必须由插入数据的应用程序生成。也就是说,如果使用以上程序插入新记录,则必须为 CustomerID 自行创建由五个字符组成的值。

在真实软件中,为新记录自动生成主键更为常见。主键通常是按顺序分配的长整数。

为新记录设置主键有两种基本技术。应用程序可调用生成下一个可用 ID 的存储过程,然后将此 ID 直接放到DataSet的新行中。或者,用于插入记录的存储过程可以为记录派生新 ID,然后将其作为返回值传递回应用程序。

第一种技术需要一点额外的逻辑来获取新 ID 并将其放到新记录的相应位置。使用存储过程执行插入操作以上示例类似。

但第二种技术要求在存储过程中使用一种新型参数。到目前为止我们见到的所有参数都是默认类型,即输入参数。实际上参数分四种类型:

InputInputOutputOutputReturnValue

存储过程为主键生成新值后,通常使用存储过程中的RETURN语句返回该值,因此用来访问该值的参数类型是ReturnValue参数。

ReturnValue参数其他类型的参数有一个重要的区别。通常,在 中为Command对象配置的参数的顺序并不重要。参数名称只用来存储过程中相应的参数相匹配。但是,对于ReturnValue参数,它必须是列表中的第一个参数。

也就是说,为Command对象配置ReturnValue参数时,必须首先在代码中配置该参数,这样它才能获取集合中的第一个数字索引。如果先配置任何其他参数,ReturnValue参数将不能正常工作。

为了说明带返回值的存储过程的用法,我们编写一个在 Northwind Products 表中插入记录的示例。此表被设置为使用 Identity 列自动创建新产品 ID。遗憾的是,Northwind 示例数据库不包含执行所需操作的存储过程,所以在完成示例其余部分之前,我们需要向数据库插入一个这样的存储过程。

转到 Visual Studio .NET 中的Server Explorer(服务器资源管理器)。打开SQLServer 的节点,打开SQLServer 实例的节点,然后打开 Northwind 数据库的节点。

右键单击Stored Procedures(存储过程)节点,选择New Stored Procedure(新建存储过程)。在出现的编辑窗口中,用以下文本替换其中的所有文本:

ALTER PROCEDURE dbo.MSDNInsertProduct(@ProductName nvarchar(40),@SupplierID int,@CategoryID int,@QuantityPerUnit nvarchar(20),@UnitPrice money,@UnitsInStock smallint,@UnitsOnOrder smallint,@ReorderLevel smallint,@Discontinued bit)ASdeclare @ProductID intSET 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 = @@IDENTITYRETURN @ProductID

现在关闭编辑窗口,当系统询问您是否要保存更改时,单击Yes(是)。现在存储过程就已保存到数据库中,并被命名为MSDNInsertProduct

现在便可以编写代码来使用此存储过程。新建 Windows 应用程序,在空白 Form1 上,放置锚定到所有四个边的DataGrid,还需添加名为btnFillbtnInsertProduct的两个按钮。将btnFillText属性设置为Fill,将btnInsertProductText属性设置为Insert Product

btnFill的 Click 事件中,放置以下代码:

Dim sConnectionString As String = _"server=localhost;uid=sa;pwd=;database=Northwind"Dim sSQL As String = "SELECT * FROM Products"Dim daGetProducts As New SqlDataAdapter(sSQL, sConnectionString)Dim dsProducts As New DataSet()daGetProducts.Fill(dsProducts, "Products")DataGrid1.DataSource = dsProducts

本文前面所讲的代码大致相同,所以我们不再赘述。不要忘记必要时更改连接字符串,并在项目代码的顶部为SQLClient命名空间放置Imports语句。然后在btnInsertProductClick事件中放置以下代码:

Dim sConnectionString As String = _"server=localhost;uid=sa;pwd=;database=Northwind"Dim cnNorthwind As New SqlConnection(sConnectionString)Dim cmdInsertProduct As New SqlCommand("MSDNInsertProduct", cnNorthwind)mandType = CommandType.StoredProcedure' 为存储过程设置参数 cmdInsertProduct.Parameters.Add(New SqlParameter("@RETURN_VALUE", SqlDbType.Int, 4, "ProductID"))cmdInsertProduct.Parameters("@RETURN_VALUE").Direction = ParameterDirection.ReturnValuecmdInsertProduct.Parameters.Add(New SqlParameter("@ProductName", _SqlDbType.NVarChar, 40, "ProductName"))cmdInsertProduct.Parameters.Add(New SqlParameter("@SupplierID", _SqlDbType.Int, 4, "SupplierID"))cmdInsertProduct.Parameters.Add(New SqlParameter("@CategoryID", _SqlDbType.Int, 4, "CategoryID"))cmdInsertProduct.Parameters.Add(New SqlParameter("@QuantityPerUnit", _SqlDbType.NVarChar, 20, "QuantityPerUnit"))cmdInsertProduct.Parameters.Add(New SqlParameter("@UnitPrice", _SqlDbType.Money, 8, "UnitPrice"))cmdInsertProduct.Parameters.Add(New SqlParameter("@UnitsInStock", _SqlDbType.SmallInt, 2, "UnitsInStock"))cmdInsertProduct.Parameters.Add(New SqlParameter("@UnitsOnOrder", _SqlDbType.SmallInt, 2, "UnitsOnOrder"))cmdInsertProduct.Parameters.Add(New SqlParameter("@ReorderLevel", _SqlDbType.SmallInt, 2, "ReorderLevel"))cmdInsertProduct.Parameters.Add(New SqlParameter("@Discontinued", _SqlDbType.Bit, 1, "Discontinued"))Dim daInsertProduct As New SqlDataAdapter()daInsertProduct.InsertCommand = cmdInsertProductDim dsProducts As DataSet = CType(DataGrid1.DataSource, DataSet)Dim drNewProduct As DataRowdrNewProduct = dsProducts.Tables("Products").NewRowdrNewProduct.Item("ProductName") = "Billy's Sesame Oil"drNewProduct.Item("SupplierID") = 4drNewProduct.Item("CategoryID") = 7drNewProduct.Item("QuantityPerUnit") = "6 10oz bottles"drNewProduct.Item("UnitPrice") = 69drNewProduct.Item("UnitsInStock") = 12drNewProduct.Item("UnitsOnOrder") = 0drNewProduct.Item("ReorderLevel") = 6drNewProduct.Item("Discontinued") = FalsedsProducts.Tables("Products").Rows.Add(drNewProduct)daInsertProduct.Update(dsProducts.Tables("Products"))MsgBox(drNewProduct.Item("ProductID"))

此代码如上所示的代码类似,只是为返回值配置参数的代码行不同。请注意,它是第一个参数,并被设置为将返回值放回到ProductID字段中。

用于向数据集中插入新行的代码是标准 代码,所以我们就不再赘述。它为产品记录创建一行新的适当结构(使用产品DataTableNewRow方法),然后将数据放入行中,最后向产品DataTable的 Rows 集合中添加行。

现在运行程序进行测试。单击Fill按钮,但不对网格中的数据进行任何更改。然后按Insert Product按钮。将插入 Billy's Sesame Oil 的新产品记录,并且出现的消息框会通知您为其返回的ProductID。还可以打开网格中的 Products 表,滚动到底部,并看到已添加了新行。

使用 Server Explorer(服务器资源管理器)编写参数代码

以上代码编写起来既冗长又繁琐。但是,DataAdapter Configuration Wizard(数据适配器配置向导)提示可以使用 Visual Studio 为我们编写此代码。DataAdapter Configuration Wizard(数据适配器配置向导)为完整配置所需的四个存储过程(分别是SelectUpdateInsertDelete)生成了代码。假设您象以上示例一样只需要一个存储过程的代码,可以将其截短。要获得只一个存储过程通信的预先编写好的代码,只需展开 Server Explorer(服务器资源管理器)以显示需要访问的存储过程,然后将该存储过程拖到设计界面上。将看到为该存储过程创建的DataAdapterCommand对象,代码的设计器部分包含为该存储过程配置参数所需的所有代码。可以按原样使用该代码,也可以根据需要复制并调整后使用。

小结

本文中的示例仍是演示软件,但至少足以向您说明如何访问存储过程,以便您开始编写自己的真实软件。当然,您需要了解要访问的存储过程,并且可能需要向数据库管理员 (DBA) 或其他组员咨询以获取该信息。

对于复杂系统,存储过程有许多优势。希望您在本文中学到了足够的知识,可以不必担心如何开始使用它们。第一次尝试编写代码时,您可能希望使用 DataAdapter Wizard(DataAdapter 向导)或 Server Explorer(服务器资源管理器)。但如果您能在必要时自行编写访问代码,则可以更有效地使用存储过程。

- 作者: shfwlj 04月19日, 星期二 11:02 回复(0) | 引用(0) 加入博采

如何在系统中统一设置对数据库的连接及调用公共类对库的操作!

如何在系统中统一设置对数据库的连接及调用公共类对库的操作!

我在一些书上,经常看到一些不错了例子,单从代码上而言,没有任何的问题,但在实际开发过程中,有时却不是很实用,因为他们往往是孤立的,如果我们把它完全的拿过来又感觉非常麻烦,特别是对库反复操作时,我们写了大量的代码去只能完成几个很少的东西,真是麻烦透顶!效率很低,所以我结合了我在实际开发过程中的一点经验,和大家分享!

首先,对数据库的连接,我们可以统一写在web.config页中,这样以后系统如果需要更改连接时,我们不用到每一页中去改,只需在config中改一次就可以了,非常的方便!具体操作如下:

在web.config页中

<configuration>

<system.web>

........................................

</system.web>

<appSettings>

<add key="ConnectString" value="server=webserver;UID=sa;PWD=km;DATABASE=testservice;connect timeout=3220"/>

</appSettings>

</configuration>

以下是建立公共类Public_Class.cs 实现对数据库的操作,具体操作如下:

先在项目中添加新类,命名为Public_Class.cs (名称自定义,我这里叫Public_Class),在Public_Class中添加代码如下:

using System;

using System.Data.SqlClient ;

using System.Configuration;

using System.Data;

namespace common_function

{

public class Public_Class

{

private static string ConnectString = ConfigurationSettings.AppSettings["ConnectString"]; //找到对数据库的连接字符串

public static void Execute_Command(stringsql_str) //执行插入、删除、更新语句

{

SqlConnection myConnection = new SqlConnection(ConnectString);

SqlCommand myCommand = new SqlCommand();

try

{

myConnection.Open();

myCommand.Connection = myConnection;

mandText =sql_str;

myCommand.ExecuteNonQuery();

}

catch(Exception err)

{

throw err;

}

finally

{

myCommand.Dispose();

myConnection.Close();

myConnection.Dispose();

}

}

public static string Judge_Repeat(stringsql_str)

{ //判断表中返回的数量

SqlConnection myConnection = new SqlConnection(ConnectString);

SqlCommand myCommand = new SqlCommand (sql_str,myConnection);

myCommand.Connection.Open();

SqlDataReader Dr;

Dr=myCommand.ExecuteReader();

try

{

Dr.Read();

return Dr[0].ToString ();

}

catch(Exception err)

{

return "";

}

finally

{

Dr.Close();

myCommand.Connection.Close();

}

}

public static DataTable Get_Select_Table(stringsql_str) //执行查询,返回DataTable

{

SqlConnection myConnection;

myConnection = new SqlConnection( ConnectString);

SqlDataAdapter myCommand = new SqlDataAdapter(sql_str, myConnection);

DataSet ds = new DataSet();

myCommand.Fill(ds, "t1");

return ds.Tables ["t1"];

}

}

}

//在.cs页面中的实际应用如下

using System;

using System.Collections;

using ponentModel;

using System.Data;

using System.Drawing;

using System.Web;

using System.Web.SessionState;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using System.Data.SqlClient;

using System.Configuration;

using common_function;

//在DataGrid中查询显示

private void first_BindGrid()

{

stringsql_str="select * from repair_instead_detail ";

MyDataGrid.DataSource =Public_Class.Get_Select_Table(sql_str);//此时返回值为DataTable类型

MyDataGrid.DataBind();//绑定显示

}

//新增数据并判断是否有重复项

private void insert()

{

string instead_num=Public_Class.Judge_Repeat("select count(*) from repair_instead_master where instead_no='"+Txt_code.Text+"'");

if(instead_num!="0")

{

Response.Write ("<script language=javascript>alert('此退机单号已存在!')</script>");

return;

}

stringsql_insert="insert into repair_instead_master values(...........)"; //sql语句

Public_Class.Execute_Command(sql_insert); //调用公共类,执行添加数据!如果是删除和更新,用法同新增一样。

}

- 作者: shfwlj 04月19日, 星期二 09:23 回复(0) | 引用(0) 加入博采

/wsyl_czl/category/86378.aspx?PageNumber
/wsyl_czl/category/86378.aspx?PageNumber=1

/wsyl_czl/category/86378.aspx?PageNumber=1

- 作者: shfwlj 04月18日, 星期一 17:31 回复(0) | 引用(0) 加入博采

将数据库中的image字段存成文档
将数据库中的image字段存成文档

代码如下

<%

dbstr="provider=sqloledb;user id=sa;password=;datasource=127.0.0.1;initial catalog=e_archive;persist security info=false"

Dim Conn

Set Conn=server.CreateObject ("ADODB.CONNECTION")

Conn.Open dbstr

set rs=conn.execute("select * from files where fid=86")

Set oFileStream = CreateObject ("ADODB.Stream")

oFileStream.Type = 1

set id=rs("fid")

set image=rs("fcontent")

do while rs.eof=false

oFileStream.Open

oFileStream.Write image.value

oFileStream.SaveToFile "d:/image"& id &".doc",2

oFileStream.close

rs.movenext

loop

set oFileStream=nothing

set image=nothing

set id=nothing

rs.close

set rs=nothing

%>

- 作者: shfwlj 04月7日, 星期四 10:10 回复(0) | 引用(0) 加入博采

在页面中使用SolpartMenu控件
/htm/center/prog/02_12_12_2.asp

微软为提供了大量的可以免费使用的控件,包括常用的表单验证控件、日历控件等。但微软似乎忘记了另外一个非常有用的控件,那就是菜单控件。的确,在网页中适当地使用菜单,不仅可以降低版面布局的工作

量,而且为日后栏目的扩充留有充分的余地。网上虽然也有不少用JavaScript脚本开发的菜单源代码,但它们并不能"即拿即用",必须针对实际情况进行二次开发,工作量大且不说,能否适用也还是个未知数,更别提以后的维护扩充了。本文所介绍的SolpartMenu菜单控件,是一个基于.NET平台的菜单控件,它功能强大,操作简单,而且完全免费,可以说是目前最好的.NET菜单控件。

SolpartMenu控件由Jon Henning开发,目前最新版本是1.0.0.4。我们先来熟悉一下它的特点。

完全支持,无缝集成。

支持XML文件和代码生成菜单。

使用数据岛,支持客户端缓冲功能(此功能仅适用于IE 5以上版本)。

支持鼠标悬停效果。

支持十几种滤镜效果(某些滤镜效果仅支持IE 5.5以上版本)。

菜单智能化展开,完全避免子菜单在展开时会显示在屏幕外。

支持自定义菜单项风格。

支持菜单在页面中自动移动。

其它特点笔者不再一一介绍,请大家参见下文第一部分中的SolpartMenu属性一览表。笔者将以下图为范例,向大家讲述分别用XML文件和代码生成菜单的方法。

在中使用SolpartMenu控件之前,我们必须要做两项准备工作:

1、 将SolpartMenu控件引用添加到"解决方案资源管理器"中。步骤如下:打开【解决方案资源管理器】面板,用鼠标右键单击【引用】,选择添加【引用..... 】菜单,在弹出的对话框中选择【.NET】卡片,单击【浏览】按钮,找到SolpartWebControls.dll文件,再依次单击【选择】,【确定】按钮。这样,SolpartMenu的引用就被添加到当前项目中。请参见下图

2、 将SolpartMenu控件添加到工具箱中,以后在应用的时候只要直接用鼠标把它拖拽到页面中即可。打开【工具箱】面板,鼠标右键单击,选择【自定义工具箱......】菜单,在弹出的对话框中选择【.NET框架组件】,单击【浏览】按钮,找到SolpartWebControls.dll文件,单击【确定】按钮即可。添加后的工具箱如右图所示。

一、 SolpartMenu控件的属性

SolpartMenu控件有许多属性,这些属性的组合应用构成了菜单的主体。下表是SolpartMenu控件的所有属性及其说明。

二、 使用XML文件生成菜单

使用XML文件生成菜单比较简单,它适用于菜单项更新不是很频繁的情况。我们只要制作一个XML文件,然后将SolpartMenu控件属性MenuDataXMLFileName指向它就可以了。具体步骤如下:

1、 打开工具箱,用鼠标将SolpartMenu控件拖拽到页面中,系统会自动为该控件赋ID值为SolpartMenu1。

2、 选中控件SolpartMenu1,打开属性面板,进行属性设置。本文中具体设置如下:

<cc1:SolpartMenu

id="SolpartMenu1"

MenuBarWidth="100px"

runat="server"

MenuDataXMLFileName="ycfcMenu.xml"

Font-Size="9pt"

MenuEffects-Style="filter:progid:DXImageTransform.Microsoft.Shadow(color='DimGray', Direction=135,

Strength=3)progid:DXImageTransform.Microsoft.Alpha(opacity=100) ;"

MenuEffects-MouseOverDisplay="Highlight"

MenuEffects-MouseOverExpand="True"

IconBackgroundColor="ActiveBorder"

MenuBorderWidth="0"

MenuBarHeight="20"

IconWidth="23"

MenuItemHeight="20"

SelectedColor="LightBlue"

BackColor="White"

ForeColor="#666666"

MenuEffects-MenuTransition="AlphaFade"

SelectedForeColor="White"

ForceDownlevel="False"

ShadowColor="Gainsboro">

</cc1:SolpartMenu>

3、 制作XML文件,并以ycfcMenu.xml为文件名保存在当前目录下。

在制作XML文件时,需要注意三点:

(1)菜单项的定义必须以根元素<root>开始,以</root>结束。

(2)XML文件中每个标记都必须成双成对地出现,即有开始标记同时必须有结束标记。

(3)每个菜单项(menuitem)都有一个唯一的ID值,它可以由字母、数字或字母数字的组合构成,但绝对不能重复。

XML文件(ycfcMenu.XML)全部内容如下:

<root>

<menuitem id="1" title="简体中文">

<menuitem id="11" title="公司信息" image="0416ycfcMenu_introduction.gif">

<menuitem id="111" title="仪化简介" url="static/introduction.aspx" />

<menubreak />

<menuitem id="112" title="业务信息" url="static/operation.aspx" />

</menuitem>

<menubreak />

<menuitem id="12" title="投资者关系">

<menuitem id="121" title="临时报告" url="static/investor.aspx" />

<menubreak />

<menuitem id="122" title="定期报告" url="static/annual.aspx" />

<menubreak />

<menuitem id="123" title="公司推介" url="static/spread.aspx" />

<menubreak />

<menuitem id="124" title="仪化股票">

<menuitem id="1241" title="仪化A股最新价格" url="static/ycstock.aspx" image="0416ycfcMenu_ycstock.gif" />

<menubreak />

<menuitem id="1242" title="仪化H股最新价格" url="static/ycstock_h.aspx" />

</menuitem>

<menubreak />

<menuitem id="125" title="联系方式" url="static/investor_contact.aspx" image="0416ycfcMenu_contact.gif" />

</menuitem>

<menubreak />

<menuitem id="13" title="信息化建设" url="static/information.aspx" image="0416ycfcMenu_information.gif" />

<menubreak />

<menuitem id="14" title="人事教育" url="static/education.aspx" />

<menubreak />

<menuitem id="15" title="环保安全" url="static/environment.aspx" />

</menuitem>

<menuitem id="2" title="法律声明" url="static/legal.aspx" />

<menuitem id="3" title="网站地图" url="static/sitemap.aspx" />

<menuitem id="4" title="联合报价">

<menuitem id="41" title="仪征化纤等六家单位POY联合报价单" url="static/POYPrice.aspx" image="0416ycfcMenu_ycstock.gif" />

<menubreak />

<menuitem id="42" title="中国石化四家单位FDY联合报价单" url="static/FDYPrice.aspx" image="0416ycfcMenu_ycstock.gif" />

</menuitem>

</root>

此时用IE浏览器浏览本页,您就可以看到本文中一模一样的菜单。以后如果需要对菜单进行修改或扩充,只要对XML文件进行适当的修改就可以了。

三、 使用代码隐藏文件动态生成菜单

用XML文件生成菜单不同,使用代码生成菜单适应于菜单重新比较频繁的情况。我们可以将菜单信息放在数据库中,通过读取数据库来动态地生成菜单。以后菜单的更新只需对数据库进行操作就可以了,无须改动源代码。

1、 打开工具箱,用鼠标将SolpartMenu控件拖拽到页面中,系统会自动为该控件赋ID值为SolpartMenu1。

2、选中控件SolpartMenu1,打开属性面板,进行属性设置。本文中具体设置如下:

<cc1:SolpartMenu

id="SolpartMenu1"

MenuBarWidth="100px"

runat="server"

Font-Size="9pt"

MenuEffects-Style="filter:progid:DXImageTransform.Microsoft.Shadow(color='DimGray', Direction=135,

Strength=3) progid:DXImageTransform.Microsoft.Alpha(opacity=100) ;"

MenuEffects-MouseOverDisplay="Highlight"

MenuEffects-MouseOverExpand="True"

IconBackgroundColor="ActiveBorder"

MenuBorderWidth="0"

MenuBarHeight="20"

IconWidth="23"

MenuItemHeight="20"

SelectedColor="LightBlue"

BackColor="White"

ForeColor="#666666"

MenuEffects-MenuTransition="AlphaFade"

SelectedForeColor="Transparent"

SystemScriptPath="scripts/">

</cc1:SolpartMenu>

请注意到用代码和用XML生成菜单时属性设置的差别。在用XML生成菜单时,我们必须给出属性MenuDataXMLFileName的值,这个值就是XML文件名;而在此这里,我们必须为SystemScriptPath属性赋值,因为在用代码生成菜单时会调用一些随控件同时分发的脚本文件,该属性的值就是这些脚本文件所在的路径,默认为scripts子目录。

2、 打开代码分离文件.asp.vb,在函数Page_Load()中输入以下代码:

‘定义objItem为XML节点

Dim objItem As System.Xml.XmlNode

‘添加第一个菜单项(简体中文),同时指定其ID值为1

objItem = SolpartMenu1.AddMenuItem("1", "简体中文", "")

‘为"简体中文"项增加一个子菜单,ID值为11,带图标

SolpartMenu1.AddMenuItem(objItem, "11", "公司信息", "", "0416ycfcMenu_introduction.gif")

‘增加一条菜单分隔线,根据参数为1决定其位置在"简体中文"的子菜单中

SolpartMenu1.AddBreak("1")

‘为"公司信息"增加一个三级子菜单"仪化简介",指定链接地址,不带图标

SolpartMenu1.AddMenuItem("11", "111", "仪化简介", "static/introduction.aspx")

‘增加一条菜单分隔线,根据参数为11决定其位置在"公司信息"的子菜单中

SolpartMenu1.AddBreak("11")

‘以下语句上面相同,不再注释

SolpartMenu1.AddMenuItem("11", "112", "业务信息", "static/operation.aspx")

SolpartMenu1.AddMenuItem(objItem, "12", "投资者关系", "")

SolpartMenu1.AddMenuItem("12", "121", "临时报告", "static/investor.aspx")

SolpartMenu1.AddBreak("12")

SolpartMenu1.AddMenuItem("12", "122", "定期报告", "static/annual.aspx")

SolpartMenu1.AddBreak("12")

SolpartMenu1.AddMenuItem("12", "123", "公司推介", "static/spread.aspx")

SolpartMenu1.AddBreak("12")

SolpartMenu1.AddMenuItem("12", "124", "仪化股票", "")

SolpartMenu1.AddMenuItem("124", "1241", "仪化A股最新价格", "static/ycstock.aspx")

SolpartMenu1.AddBreak("124")

SolpartMenu1.AddMenuItem("124", "1242", "仪化H股最新价格", "static/ycstock_h.aspx")

SolpartMenu1.AddBreak("12")

SolpartMenu1.AddMenuItem("12", "125", "联系方式", "static/investor_contact.aspx")

SolpartMenu1.AddBreak("1")

SolpartMenu1.AddMenuItem(objItem, "13", "信息化建设", "static/information.aspx", "0416ycfcMenu_information.gif")

SolpartMenu1.AddBreak("1")

SolpartMenu1.AddMenuItem(objItem, "14", "人事教育", "static/education.aspx")

SolpartMenu1.AddBreak("1")

SolpartMenu1.AddMenuItem(objItem, "15", "环保安全", "static/environment.aspx")

objItem = SolpartMenu1.AddMenuItem("2", "法律声明", "static/legal.aspx")

objItem = SolpartMenu1.AddMenuItem("3", "网站地图", "static/sitemap.aspx")

objItem = SolpartMenu1.AddMenuItem("4", "联合报价", "")

SolpartMenu1.AddMenuItem(objItem, "41", "仪征化纤等六家单位POY联合报价单", "static/POYprice.aspx", "0416ycfcMenu_ycstock.gif")

SolpartMenu1.AddBreak(objItem)

SolpartMenu1.AddMenuItem(objItem, "42", "中国石化等四家单位FDY联合报价单", "static/FDYprice.aspx")

以上的代码比较简单,应该不难理解,笔者在此就不再多述。

从SolpartMenu控件的使用过程和使用效果来看,它的确是一款非常优秀的菜单控件,无论是用XML文件来生成菜单,还是在代码中生成动态菜单,对以后的修改或扩充都非常方便。当然我看重的不仅仅是它强大的功能,最重要的一点在于它是完全免费的,我们无须支付任何费用就可以无任何限制地使用。另外一点,由于它是基于.NET平台的,所以您只能在页面中使用它,而不能用于ASP等其他页面。

SolpartMenu控件的下载地址是:/techcorner/SolpartMenuHistory.aspx,您在这里可以随时跟踪它的更新信息,下载最新版本。当然您也可以给作者写信,向他提出你使用后的建议和意见。

- 作者: shfwlj 03月30日, 星期三 16:08 回复(0) | 引用(0) 加入博采

IIS出问题:Asp的网页无法调用访问

1、iis不能解析asp

2、iis不支持此接口

第1楼博客论坛网友: linteacher 发表于 04年6月17日 9:10 [回复数]:4 [点击数]:451本主题URL地址为:/p2548.html第2楼博客论坛网友: 游客 发表于 04年6月17日 12:29第3楼博客论坛网友: Xinsoft 发表于 04年6月18日 5:27第4楼博客论坛网友: 吹笛到天明 发表于 04年6月23日 15:04

如果觉得《ADO.NET使用经验集》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。