介绍

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

  本文包括:

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

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

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

  l XML集成的信息

  l 一般的技巧

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

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

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

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

  连接到SQL Server 7.0及以上版本

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

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

  连接到ODBC数据源

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

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

使用DataReader、DataSet、DataAdapter和DataView

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

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

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

  DataSet与DataReader的对比

  在设计应用程序时,决定使用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属性将导致至少两次建立索引。

  分页

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

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

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

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

SELECT TOP 10 * FROM
(SELECT TOP 30 * FROM Customers ORDER BY Id ASC) AS Table1
ORDER BY Id DESC

  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语句

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

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

  使用多个表填充数据集  

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

'Visual Basic
Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers;
SELECT * FROM Orders;", myConnection)
Dim ds As DataSet = New DataSet()
da.Fill(ds, "Customers")
//C#
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers;
SELECT * FROM Orders;", myConnection);
DataSet ds = new DataSet();
da.Fill(ds, "Customers");

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

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

'Visual Basic
Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers;
SELECT * FROM Orders;", myConnection)
da.TableMappings.Add("Customers1", "Orders")
Dim ds As DataSet = New DataSet()
da.Fill(ds, "Customers")
//C#
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers;
SELECT * FROM Orders;", myConnection);
da.TableMappings.Add("Customers1", "Orders");
DataSet ds = new DataSet();
da.Fill(ds, "Customers");

  使用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隐性地为游标的生命周期的数据存储打开第二个不在连接池中的连接,接着隐性地关闭它。在ADO.NET中,如果你想在同一个数据存储上同时打开两个DataReader,你必须明确地建立两个连接,每个DataReader一个。通过这种方法ADO.NET给了你对连接池使用的更多控制。

  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)

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

  使用OleDbCommand的最好经验

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

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

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

  使用SqlCommand的经验

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

  Prepare方法的使用

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

  明确地指定大纲和元数据

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

  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参数都为空的行:

SELECT * FROM Customers
WHERE ((LastName = @LastName) OR (LastName IS NULL AND @LastName IS NULL))

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

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

'Visual Basic
Dim param As SqlParameter = New SqlParameter("@Name", SqlDbType.NVarChar, 20)
param.Value = DBNull.Value
//C#
SqlParameter param = new SqlParameter("@Name", SqlDbType.NVarChar, 20);
param.Value = DBNull.Value;

  执行事务

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

  使用连接

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

  连接池

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

  用DataAdapter优化连接

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

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

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

'Visual Basic
Public Sub RunSqlTransaction(da As SqlDataAdapter,
myConnection As SqlConnection, ds As DataSet)
myConnection.Open()
Dim myTrans As SqlTransaction = myConnection.BeginTransaction()
myCommand.Transaction = myTrans
Try
da.Update(ds)
myTrans.Commit()
Console.WriteLine("Update successful.")
Catch e As Exception
Try
myTrans.Rollback()
Catch ex As SqlException
If Not myTrans.Connection Is Nothing Then
Console.WriteLine("An exception of type "
& ex.GetType().ToString() & _
" was encountered while attempting to roll back the transaction.")
End If
End Try
Console.WriteLine("An exception of type
" & e.GetType().ToString() & " was encountered.")
Console.WriteLine("Update failed.")
End Try
myConnection.Close()
End Sub
//C#
public void RunSqlTransaction(SqlDataAdapter da,
SqlConnection myConnection, DataSet ds)
{
myConnection.Open();
SqlTransaction myTrans = myConnection.BeginTransaction();
myCommand.Transaction = myTrans;
try
{
da.Update(ds);
myCommand.Transaction.Commit();
Console.WriteLine("Update successful.");
}
catch(Exception e)
{
try
{
myTrans.Rollback();
}
catch (SqlException ex)
{
if (myTrans.Connection != null)
{
Console.WriteLine("An exception of type " + ex.GetType() +
" was encountered while attempting to roll back the transaction.");
}
}
Console.WriteLine(e.ToString());
Console.WriteLine("Update failed.");
}
myConnection.Close();
}

  经常关闭连接(Connection)和DataReader

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

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

  在C#中使用"Using"语句

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

//C#
string connString = "Data Source=localhost;
Integrated Security=SSPI;Initial Catalog=Northwind;";
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers";
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
Console.WriteLine("{0}/t{1}", dr.GetString(0), dr.GetString(1));
}
}

  避免访问OleDbConnection.State属性

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

  XML集成

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

  数据集与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查询服务的SQL Server

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

  SQLXML可管理类

  在.NET框架组件中有一些类暴露了XML为SQL Server 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条件检测来验证行中所有的源列值数据库中的匹配。

  多线程编程

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

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

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

避免自动增加(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条件检测来验证行中所有的源列值数据库中的匹配。

  多线程编程

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

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

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

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

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

ADO.NET 简介

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

简而言之,DataSet 在 ADO.NET 中用作数据容器,并在数据库断开连接时使用。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:用于访问存储过程的主要 ADO.NET 类以及它们之间的关系

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

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

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 对象。事实上,没有这些对象,ADO.NET 便无法工作,但它们是在后台创建并使用的。实例化 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)
cmdProducts.CommandType = CommandType.StoredProcedure
Dim 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)
cmdOrders.CommandType = CommandType.StoredProcedure
' 为存储过程设置参数
Dim prmCustomerID As New SqlParameter()
prmCustomerID.ParameterName = "@CustomerID"
prmCustomerID.SqlDbType = SqlDbType.VarChar
prmCustomerID.Size = 5
prmCustomerID.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)
cmdOrders.CommandType = CommandType.StoredProcedure
cmdOrders.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")

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

使用存储过程更新数据库

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

在第一个示例中,我们将使用 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:MSDNSelectCustomers
  • Update:MSDNUpdateCustomer
  • Insert:MSDNInsertCustomer
  • Delete: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)时将出现异常,因为在 SQL Server 中不能更新数据库记录中的主键。

查看由向导生成的代码会有所帮助,所有这些代码最初都隐藏在 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 的 Customers DataTable此参数对应的字段。这使 DataTable 中的值在插入操作期间自动插入到参数的 Value 属性中。让我们来详细介绍一下。

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

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

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

从存储过程返回值

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

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

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

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

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

Input

此参数只用于将信息从应用程序传输到存储过程。

InputOutput

此参数可将信息从应用程序传输到存储过程,并将信息从存储过程传输回应用程序。

Output

此参数只用于将信息从存储过程传输回应用程序。

ReturnValue

此参数表示存储过程的返回值。SQL Server 的存储过程参数列表中不显示该参数。它只存储过程的 RETURN 语句中的值相关联。

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

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

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

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

转到 Visual Studio .NET 中的 Server Explorer(服务器资源管理器)。打开 SQL Server 的节点,打开 SQL Server 实例的节点,然后打开 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
)
AS
declare @ProductID int
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 = @@IDENTITY
RETURN @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)
cmdInsertProduct.CommandType = CommandType.StoredProcedure
' 为存储过程设置参数
cmdInsertProduct.Parameters.Add(New SqlParameter("@RETURN_VALUE", SqlDbType.Int, 4, "ProductID"))
cmdInsertProduct.Parameters("@RETURN_VALUE").Direction = ParameterDirection.ReturnValue
cmdInsertProduct.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 = cmdInsertProduct
Dim dsProducts As DataSet = CType(DataGrid1.DataSource, DataSet)
Dim drNewProduct As DataRow
drNewProduct = dsProducts.Tables("Products").NewRow
drNewProduct.Item("ProductName") = "Billy's Sesame Oil"
drNewProduct.Item("SupplierID") = 4
drNewProduct.Item("CategoryID") = 7
drNewProduct.Item("QuantityPerUnit") = "6 10oz bottles"
drNewProduct.Item("UnitPrice") = 69
drNewProduct.Item("UnitsInStock") = 12
drNewProduct.Item("UnitsOnOrder") = 0
drNewProduct.Item("ReorderLevel") = 6
drNewProduct.Item("Discontinued") = False
dsProducts.Tables("Products").Rows.Add(drNewProduct)
daInsertProduct.Update(dsProducts.Tables("Products"))
MsgBox(drNewProduct.Item("ProductID"))

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

用于向数据集中插入新行的代码是标准 ADO.NET 代码,所以我们就不再赘述。它为产品记录创建一行新的适当结构(使用产品 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 2005年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(string sql_str)        //执行插入、删除、更新语句
  {
   SqlConnection myConnection = new SqlConnection(ConnectString);
   SqlCommand myCommand = new SqlCommand();
   try
   {
    myConnection.Open();
    myCommand.Connection = myConnection;
    myCommand.CommandText = sql_str;
    myCommand.ExecuteNonQuery();
   
   }
    
   catch(Exception err)
   {
    throw err;
   }
   finally
   {
    myCommand.Dispose();
    myConnection.Close();
    myConnection.Dispose();
   }

}

public static string  Judge_Repeat(string sql_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(string sql_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 System.ComponentModel;
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()  
  {
   string sql_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;
    }

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

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

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

http://blog.csdn.net/wsyl_czl/category/86378.aspx?PageNumber
http://blog.csdn.net/wsyl_czl/category/86378.aspx?PageNumber=1
http://blog.csdn.net/wsyl_czl/category/86378.aspx?PageNumber=1

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

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

代码如下

<%
dbstr="provider=sqloledb;user id=sa;password=;data source=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 2005年04月7日, 星期四 10:10  回复(0) |  引用(0) 加入博采

在ASP.NET页面中使用SolpartMenu控件
http://www.ccw.com.cn/htm/center/prog/02_12_12_2.asp

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

 

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

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

  • 完全支持ASP.NET,VS.NET无缝集成。

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

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

  • 支持鼠标悬停效果。

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

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

  • 支持自定义菜单项风格。

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

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

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

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

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

    一、 SolpartMenu控件的属性

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

    布局

     

    IconWidth 菜单项图标区宽度
    MenuBarHeight 菜单条高度
    MenuBorderWidth 菜单边框宽度
    MenuItemHeight 菜单项高度
    数据

     

    (DataBindings) 相关联的数据绑定的集合
    MenuData 生成菜单的XML字串
    MenuDataXMLFileName 包含菜单内容的XML文件名
    外观

     

    ArrowImage 指示下级子菜单的箭头图像
    BackColor 菜单背景色
    BackgroundMenuImage 菜单背景图像
    Display 水平|垂直显示菜单
    Font
        Bold 字体加粗(True|False)
        Italic 字体倾斜(True|False)
        Name 首选字体名称
        Names 可选的字体名称序列
        Overline 字体上划线(True|False)
        Size 字体大小(磅)
        Strikeout 字体删除线(True|False)
        Underline 字体下划线(True|False)
    ForceDownlevel 用文字表示当前菜单层级(True|False)
    ForeColor 菜单前景色
    HighlightColor 菜单高亮色
    IconBackGroundColor 图标区背景色
    MenuAlignment 菜单对齐方式
    MenuEffects
         MenuTransition 菜单展开滤镜效果
         MenuTransitionLength 滤镜效果的时间(秒)
         MouseOverDisplay 鼠标悬停时菜单条效果
         MouseOverExpand 鼠标悬停是否展开菜单(True|False)
         ShadowColor 菜单阴影效果
         ShadowDirection 菜单阴影方向
         ShadowStrength 菜单阴影强度
    RootArrow 菜单条箭头(推荐用于垂直显示菜单时)
    SelectedBorderColor 选中菜单项边框颜色
    SelectedColor 选中菜单项的背景色
    SelectedForeColor 选中菜单项的前景色
    ShadowColor 菜单项右、下边框颜色
    行为

     

    AccessKey 键盘的快捷方式
    Enabled 菜单激活状态(True|False)
    EnableViewState 是否自动保存菜单状态(True|False)
    Moveable 菜单能否移动(True|False)
    TabIndex 菜单的TAB键顺序
    ToolTip 鼠标悬停时的文字说明
    Visible 菜单是否可见(True|False)
    杂项

     

    (ID) 菜单的ID
    SystemImagesPath 菜单中图片路径
    SystemScriptPath 菜单脚本路径(用于代码生成菜单)

    二、 使用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.NET页面中使用它,而不能用于ASP等其他页面。

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

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

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

    1、iis不能解析asp

    2、iis不支持此接口

    IIS出问题:Asp的网页无法调用访问
     
     
    第1楼  博客论坛网友: linteacher 发表于 04年6月17日 9:10 [回复数]:4 [点击数]:451
      本主题URL地址为:http://bbs.blogchina.com/p2548.html
     
      Win2000服务器作为网站Web服务器,现在Asp的网页都无法调用访问,提示http://500内部错误,但纯htm文件可以访问,用Serv-U搭建的Ftp服务也还可以用。想对IIS重启,它竟然说:"不支持此接口"!!

      伴随这个还有这样问题,系统日志纪律很快就满了,提示:WINS TCP 侦听器线程遇到一个错误。

      我想,应该是IIS遭攻击被破坏了吧?我卸载掉再重装IIS,还是无法解决问题。

      请各位高手们赐教呀!


      欢迎访问:我们论坛@泉港五中 http://www.qgweb.net/bbs http://www.qgwz.com
     
     
    第2楼  博客论坛网友: 游客 发表于 04年6月17日 12:29
     
      在这里问技术问题?

      
     
     
    第3楼  博客论坛网友: Xinsoft 发表于 04年6月18日 5:27
     
      可能是 IWAM_<Hostname> 这个用户出了问题

      你再卸载一下 IIS ,卸完了先删除 IWAM_<Hostname> 和 IUSR_<Hostname> 这两个用户,再重新安装一下 IIS 。


      http://xinsoft.blogchina.com 应用之美,在于药到病除
     
     
    第4楼  博客论坛网友: 吹笛到天明 发表于 04年6月23日 15:04
     
      IIS5 HTTP500内部错误解决办法

      日期:星期日 2004 03 07 作者: 人气:4279 查看:[大字体 中字体 小字体]

      一.错误表现

      iis5的http 500内部服务器错误是我们经常碰到的错误之一,它的主要错误表现就是asp程序不能浏览但htm静态网页不受影响。另外当错误发生时,系统事件日志和安全事件日志都会有相应的记录。

      具体如下:

      (一)ie中的表现

      当浏览以前能够正常运行的asp页面时会出现如下的错误:

      网页无法显示

      您要访问的网页存在问题,因此无法显示。

      请尝试下列操作:

      打开 http://127.0.0.1 主页,寻找指向所需信息的链接。

      单击刷新按钮,或者以后重试。

      http 500 - 内部服务器错误

      internet 信息服务

      技术信息(支持个人)

      详细信息:

      microsoft 支持

      或者是:

      server application error

      the server has encountered an error while loading an application during the processing of your request. please refer to the event log for more detail information. please contact the server administrator for assistance.

      (二)安全日志记录(2条)

      事件类型: 失败审核

      事件来源: security

      事件种类: 登录/注销

      事件 id: 529

      日期: 2001-9-9

      事件: 11:17:07

      用户: nt authority/system

      计算机: myserver

      描述:

      登录失败:

      原因: 用户名未知或密码错误

      用户名: iwam_myserver

      域: mydom

      登录类型: 4

      登录过程: advapi

      身份验证程序包: microsoft_authentication_package_v1_0

      工作站名: myserver

      事件类型: 失败审核

      事件来源: security

      事件种类: 帐户登录

      事件 id: 681

      日期: 2001-9-9

      事件: 11:17:07

      用户: nt authority/system

      计算机: myserver

      描述:

      登录到帐户: iwam_myserver

      登录的用户: microsoft_authentication_package_v1_0

      从工作站: myserver

      未成功。错误代码是: 3221225578

      (三)系统日志中的记录(2条)

      事件类型: 错误

      事件来源: dcom

      事件种类: 无

      事件 id: 10004

      日期: 2001-9-9

      事件: 11:20:26

      用户: n/a

      计算机: myserver

      描述:

      dcom 遇到错误"无法更新密码。提供给新密码的值包含密码中不允许的值。 "并且无法登录到 ./iwam_myserver 上以运行服务器:

      {3d14228d-fbe1-11d0-995d-00c04fd919c1}

      事件类型: 警告

      事件来源: w3svc

      事件种类: 无

      事件 id: 36

      日期: 2001-9-9

      事件: 11:20:26

      用户: n/a

      计算机: myserver

      描述:

      服务器未能转入应用程序 ‘/lm/w3svc/4/root‘。错误是 ‘runas 的格式必须是<域名>/<用户名>或只是<用户名>‘。

      若要获取关于此消息的更多的信息,请访问 microsoft 联机支持站点: http://www.microsoft.com/contentredirect.asp 。

      二.原因分析

      综合分析上面的错误表现我们可以看出,主要是由于iwam账号(在我的计算机即是iwam_myserver账号)的密码错误造成了http 500内部错误。

      在详细分析http500内部错误产生的原因之前,先对iwam账号进行一下简要的介绍:iwam账号是安装iis5时系统自动建立的一个内置账号,主要用于启动进程之外的应用程序的internet信息服务。iwam账号的名字会根据每台计算机netbios名字的不同而有所不同,通用的格式是iwam_machine,即由"iwam"前缀、连接线"_"加上计算机的netbios名字组成。我的计算机的netbios名字是myserver,因此我的计算机上iwam账号的名字就是iwam_myserver,这一点iis匿名账号isur_machine的命名方式非常相似。

      iwam账号建立后被active directory、iis metabase数据库和com+应用程序三方共同使用,账号密码被三方分别保存,并由操作系统负责这三方保存的iwam密码的同步工作。按常理说,由操作系统负责的工作我们大可放心,不必担心出错,但不知是bug还是其它什么原因,系统的对iwam账号的密码同步工作有时会失败,使三方iwam账号所用密码不统一。当iis或com+应用程序使用错误iwam的密码登录系统,启动iis out-of-process pooled applications时,系统会因密码错误而拒绝这一请求,导致iis out-of-process pooled applications启动失败,也就是我们在id10004错误事件中看到的"不能运行服务器{3d14228d-fbe1-11d0-995d-00c04fd919c1} "(这里{3d14228d-fbe1-11d0-995d-00c04fd919c1} 是iis out-of-process pooled applications的key),不能转入iis5应用程序,http 500内部错误就这样产生了。

      三.解决办法

      知道了导致http 500内部错误的原因,解决起来就比较简单了,那就是人工同步iwam账号在active directory、iis metabase数据库和com+应用程序中的密码。

      具体操作分三步,均需要以管理员身份登录计算机以提供足够的操作权限(iwam账号以iwam_myserver为例)。

      (一)更改active directory中iwam_myserver账号的密码

      因iwam账号的密码由系统控制,随机产生,我们并不知道是什么,为完成下面两步的密码同步工作,我们必须将iwam账号的密码设置为一个我们知道的值。

      1、选择"开始"->"程序"->"管理工具"->"active directory用户和计算机",启动"active directory用户和计算机"管理单元。

      2、单击"user",选中右面的"iwam_myserver",右击选择"重设密码(t)...",在跳出的重设密码对方框中给iwam_myserver设置新的密码,这儿我们设置成"aboutnt2001"(没有引号的),确定,等待密码修改成功。

      (二)同步iis metabase中iwam_myserver账号的密码

      可能因为这项改动太敏感和重要,微软并没有为我们修改iis metabase中iwam_myserver账号密码提供一个显式的用户接口,只随iis5提供了一个管理脚本adsutil.vbs,这个脚本位于c:/inetpub/adminscripts子目录下(位置可能会因你安装iis5时设置的不同而有所变动)。

      adsutil.vbs脚本功能强大,参数非常多且用法复杂,这里只提供使用这个脚本修改iwam_myserver账号密码的方法:

      adsutil set w3svc/wamuserpass password

      "password"参数就是要设置的iwam账号的新的密码。因此我们将iis metabase中iwam_myserver账号的密码修改为"aboutnt2001"的命令就是:

      c:/inetpub/adminscripts>adsutil set w3svc/wamuserpass "aboutnt2001"

      修改成功后,系统会有如下提示:

      wamuserpass: (string) "aboutnt2001"

      (三)同步com+应用程序所用的iwam_myserver的密码

      同步com+应用程序所用的iwam_myserver的密码,我们有两种方式可以选择:一种是使用组件服务mmc管理单元,另一种是使用iwam账号同步脚本synciwam.vbs。

      1、使用组件服务mmc管理单元

      (1)启动组件服务管理单元:选择"开始"->"运行"->"mmc",启动管理控制台,打开"添加/删除管理单元"对话框,将"组件服务"管理单元添加上。

      (2)找到"组件服务"->"计算机"->"我的电脑"->"com+应用程序"->"out-of-process pooled applications",右击"out-of-process pooled applications"->"属性"。

      (3)切换到"out-of-process pooled applications"属性对话框的"标志"选项卡。"此应用程序在下列账户下运行"选择中"此用户"会被选中,用户名是"iwam_myserver"。这些都是缺省的,不必改动。在下面的"密码"和"确认密码"文本框内输入正确的密码"aboutnt2001",确定退出。

      (4)系统如果提示"应用程序被一个以上的外部产品创建。你确定要被这些产品支持吗?"时确定即可。

      (5)如果我们在iis中将其它一些web的"应用程序保护"设置为"高(独立的)",那么这个web所使用的com+应用程序的iwam账号密码也需要同步。重复(1)-(4)步,同步其它相应out of process application的iwam账号密码。

      2、使用iwam账号同步脚本synciwam.vbs

      实际上微软已经发现iwam账号在密码同步方面存在问题,因此在iis5的管理脚本中单独为iwam账号密码同步编写了一个脚本synciwam.vbs,这个脚本位于c:/inetpub/adminscripts子目录下(位置可能会因你安装iis5时设置的不同而有所变动)。

      synciwam.vbs脚本用法比较简单:

      cscript synciwam.vbs [-v│-h]

      "-v"参数表示详细显示脚本执行的整个过程(建议使用),"-h"参数用于显示简单的帮助信息。

      我们要同步iwam_myserver账号在com+应用程序中的密码,只需要执行"cscript synciwam.vbs -v"即可,如下:

      cscript c:/inetpub/adminscripts/synciwam.vbs -v

      microsoft (r) windows script host version 5.6

      版权所有(c) microsoft corporation 1996-2000。保留所有权利。

      wamusername:iwam_myserver

      wamuserpass:aboutnt2001

      iis applications defined:

      name, appisolated, package id

      w3svc, 0, {3d14228c-fbe1-11d0-995d-00c04fd919c1}

      root, 2,

      iishelp, 2,

      iisadmin, 2,

      iissamples, 2,

      msadc, 2,

      root, 2,

      iisadmin, 2,

      iishelp, 2,

      root, 2,

      root, 2,

      out of process applications defined:

      count: 1

      {3d14228d-fbe1-11d0-995d-00c04fd919c1}

      updating applications:

      name: iis out-of-process pooled applications key: {3d14228d-fbe1-11d0-995d-00c04fd919c1}

      从上面脚本的执行情况可以看出,使用synciwam.vbs脚本要比使用组件服务的方法更全面和快捷。它首先从iis的metabase数据库找到iwam账号"iwam_myserver"并取出对应的密码"aboutnt2001",然后查找所有已定义的iis applications和out of process applications,并逐一同步每一个out of process applications应用程序的iwam账号密码。

      使用synciwam.vbs脚本时,要注意一个问题,那就是在你运行synciwam.vbs之前,必须保证iis metabase数据库active directory中的iwam密码已经一致。因为synciwam.vbs脚本是从iis metabase数据库而不是从active directory取得iwam账号的密码,如果iis metabase中的密码不正确,那synciwam.vbs取得的密码也会不正确,同步操作执行到"updating applications"系统就会报80110414错误,即"找不到应用程序{3d14228d-fbe1-11d0-995d-00c04fd919c1}"。

      好了,到现在为止,iwam账号在active directory、iis metabase数据库和com+应用程序三处的密码已经同步成功,你的asp程序又可以运行了!

      修改成功后,系统会有如下提示:

      ---------------------------------------------------

      经过测试,显示应该是

      wamuserpass: (string) "*******"

ADO.NET使用经验集相关推荐

  1. 掌握ADO.NET的十个热门技巧

    NET的数据访问编程模式需要一套新的技巧和最佳方法. ADO.NET提供了一个统一的编程模式和一组公用的类来进行任何类型的数据访问,而不管你用何种语言来开发代码.ADO.NET是全新的,但又与ADO尽 ...

  2. [No0000BC]ADO.NET中的几个主要对象

    ADO 指 ActiveX 数据对象(ActiveX Data Objects). 从一个 ASP 页面内部访问数据库的通常的方法是: 创建一个到数据库的 ADO 连接 打开数据库连接 创建 ADO ...

  3. ADO RecondsetPtr 以及如何实现对表的增加删除数据

    首先在Header Files目录下的头文件StdAfx.h中添加将msado.dll或msado.tlh动态链接库导入程序的代码:#import "C:\\Program Files\\C ...

  4. VC++使用ADO开发ACCESS数据库

    VC++使用ADO开发ACCESS数据库 ADO和ADOX到底是什么,二者的作用和区别 ADO是Microsoft 最新推出的数据库访问的高层软件接口.它和Microsoft以前的数据库访问接口DAO ...

  5. VC访问数据库学习总结

    VC连接数据库方式 目前Windows系统上常见的数据库接口包括: ODBC(开放数据库互连):访问数据库得先配置数据源 MFC(Microsoft基础类)ODBC类 DAO(数据访问对象) RDO( ...

  6. 水晶报表基础入门——1.水晶报表技术

        在程序开发中,经常要对合并计算.多级汇总.制作图表.条件格式化进行编程.在这种情况下用普通的数据控件很难完成,而水晶报表大大简化了这些工作.用其可以制作非常漂亮的图表.格式化文本,而且还可以把 ...

  7. 认识VF--Visual FoxPro 漫谈(轉)

    目录 前言 Visual FoxPro是什么 Visual FoxPro 已经过时了吗 面向对象与面向过程之争 面向记录与面向集合之争 产品定位导致Visual FoxPro变化不易为人们感觉.微软要 ...

  8. 认识VF--Visual FoxPro 漫谈

    BOE.COM Article Resource News Links About US      文章标题 Visual FoxPro 漫谈 作品来源 BOE 数据网络工作室 创建日期 2001年0 ...

  9. 将 DTS 用于业务智能解决方案的最佳实践

    摘自:http://www.microsoft.com/china/MSDN/library/archives/library/dnSQL2k/html/SQL_busintbpwithDTS.asp ...

最新文章

  1. java shiro实例_Apache Shiro入门实例
  2. PythonElementTree
  3. 判断字符串1是否在字符串2中出现的方法
  4. C# 如何调用EventLog
  5. java自学路线图_JAVA自学路线图
  6. 2013-8-9练习[多种方法制作一个计时器]
  7. 软件测试薪资标准新鲜出炉,你达标了吗?
  8. 服务器 cpu系列 至强系列那个好,至强系列 intel至强和酷睿I系列,到底哪个好
  9. 关于射频技术在粮食安全的科研进展
  10. 计算机主板 上电顺序,BIOS很熟悉,电脑开机BIOS开机自检顺序你知道吗?
  11. Word标题样式关联多级列表
  12. 线性方法求欧拉数-POJ2478
  13. android 录像 视频大小,Android相机 – 录制视频时预览放大
  14. 《点线SLAM系统》
  15. android_Socket网络编程实现手机图片上传到电脑
  16. 我家云刷android系统教程,我家云刷OMV试用体验
  17. win10命令窗口在哪里
  18. 新闻事件脉络挖掘思路
  19. WINDOWS如何使用CMD修复硬盘命令来解决硬盘问题?
  20. 【信号去噪】基于硬阈值、软阈值、半软阈值、Maxmin阈值、Garrote阈值小波变换实现心音去噪附matlab代码

热门文章

  1. 2018/7/2 卜昌凯
  2. Django数据库——迁移命令
  3. 3.17服务器维护,英雄联盟3.17停机维护公告更新时间 lol英雄联盟3月17日更新内容汇总...
  4. 一篇文章帮你顺利通过阿里ACP考试认证
  5. 在reader中勾选pdf复选框_如何让文件夹内PDF图片显示图标
  6. nyoj1249 物资调度(dp)
  7. Unity3D_控制摄像机跟随物体
  8. Spring Boot
  9. 『NLP学习笔记』Transformer技术详细介绍
  10. 换系统影响计算机名吗,鲁大师卸载会影响系统么