飞艳小屋

程序--人生--哲学___________________欢迎艳儿的加入

BlogJava 首页 新随笔 联系 聚合 管理
  52 Posts :: 175 Stories :: 107 Comments :: 0 Trackbacks
编写高性能 Web 应用程序的 10 个技巧
编写高性能 Web 应用程序的 10 个技巧
发布日期: 2/1/2005 | 更新日期: 2/1/2005

本文讨论

常见 ASP.NET 性能难点

面向 ASP.NET 的有用性能提示和技巧

在 ASP.NET 中使用数据库的建议

使用 ASP.NET 进行缓存和后台处理

本文使用了以下技术:
ASP.NET、.NET Framework 和 IIS

*
本页内容

数据层性能 数据层性能 技巧 1 — 返回多个结果集 技巧 1 — 返回多个结果集 技巧 2 — 分页的数据访问 技巧 2 — 分页的数据访问 技巧 3 — 连接池 技巧 3 — 连接池 技巧 4 — ASP.NET 缓存 API 技巧 4 — ASP.NET 缓存 API 技巧 5 — 每请求缓存 技巧 5 — 每请求缓存 技巧 6 — 后台处理 技巧 6 — 后台处理 技巧 7 — 页输出缓存和代理服务器 技巧 7 — 页输出缓存和代理服务器 技巧 8 — 运行 IIS 6.0(只要用于内核缓存) 技巧 8 — 运行 IIS 6.0(只要用于内核缓存) 技巧 9 — 使用 Gzip 压缩 技巧 9 — 使用 Gzip 压缩 技巧 10 — 服务器控件视图状态 技巧 10 — 服务器控件视图状态 小结 小结

使用 ASP.NET 编写 Web 应用程序的简单程度令人不敢相信。正因为如此简单,所以很多开发人员就不会花时间来设计其应用程序的结构,以获得更好的性能了。在本文中,我将讲述 10 个用于编写高性能 Web 应用程序的技巧。但是我并不会将这些建议仅局限于 ASP.NET 应用程序,因为这些应用程序只是 Web 应用程序的一部分。本文不作为对 Web 应用程序进行性能调整的权威性指南 — 一整本书恐怕都无法轻松讲清楚这个问题。请将本文视作一个很好的起点。

成为工作狂之前,我原来喜欢攀岩。在进行任何大型攀岩活动之前,我都会首先仔细查看指南中的路线,阅读以前游客提出的建议。但是,无论指南怎么好,您都需要真正的攀岩体验,然后才能尝试一个特别具有挑战性的攀登。与之相似,当您面临修复性能问题或者运行一个高吞吐量站点的问题时,您只能学习如何编写高性能 Web 应用程序。

我的个人体验来自在 Microsoft 的 ASP.NET 部门作为基础架构程序经理的经验,在此期间我运行和管理 www.ASP.NET,帮助设计社区服务器的结构,社区服务器是几个著名 ASP.NET 应用程序(组合到一个平台的 ASP.NET Forums、.Text 和 nGallery)。我确信有些曾经帮助过我的技巧对您肯定也会有所帮助。

您应该考虑将应用程序分为几个逻辑层。您可能听说过 3 层(或者 n 层)物理体系结构一词。这些通常都是规定好的体系结构方式,将功能在进程和/或硬件之间进行了物理分离。当系统需要扩大时,可以很轻松地添加更多的硬件。但是会出现一个与进程和机器跳跃相关的性能下降,因此应该避免。所以,如果可能的话,请尽量在同一个应用程序中一起运行 ASP.NET 页及其相关组件。

因为代码分离以及层之间的边界,所以使用 Web 服务或远程处理将会使得性能下降 20% 甚至更多。

数据层有点与众不同,因为通常情况下,最好具有专用于数据库的硬件。然而进程跳跃到数据库的成本依然很高,因此数据层的性能是您在优化代码时首先要考虑的问题。

在深入应用程序的性能修复问题之前,请首先确保对应用程序进行剖析,以便找出具体的问题所在。主要性能计数器(如表示执行垃圾回收所需时间百分比的计数器)对于找出应用程序在哪些位置花费了其主要时间也非常有用。然而花费时间的位置通常非常不直观。

本文讲述了两种类型的性能改善:大型优化(如使用 ASP.NET 缓存),和进行自身重复的小型优化。这些小型优化有时特别有意思。您对代码进行一点小小的更改,就会获得很多很多时间。使用大型优化,您可能会看到整体性能的较大飞跃。而使用小型优化时,对于某个特定请求可能只会节省几毫秒的时间,但是每天所有请求加起来,则可能会产生巨大的改善。

数据层性能

谈到应用程序的性能调整,有一个试纸性的测试可用来对工作进行优先级划分:代码是否访问数据库?如果是,频率是怎样的?请注意,这一相同测试也可应用于使用 Web 服务或远程处理的代码,但是本文对这些内容未做讲述。

如果某个特定的代码路径中必需进行数据库请求,并且您认为要首先优化其他领域(如字符串操作),则请停止,然后执行这个试纸性测试。如果您的性能问题不是非常严重的话,最好花一些时间来优化一下与数据库、返回的数据量、进出数据库的往返频率相关的花费时间。

了解这些常规信息之后,我们来看一下可能会有助于提高应用程序性能的十个技巧。首先,我要讲述可能会引起最大改观的更改。

技巧 1 — 返回多个结果集

仔细查看您的数据库代码,看是否存在多次进入数据库的请求路径。每个这样的往返都会降低应用程序可以提供的每秒请求数量。通过在一个数据库请求中返回多个结果集,可以节省与数据库进行通信所需的总时间长度。同时因为减少了数据库服务器管理请求的工作,还会使得系统伸缩性更强。

虽然可以使用动态 SQL 返回多个结果集,但是我首选使用存储过程。关于业务逻辑是否应该驻留于存储过程的问题还存在一些争议,但是我认为,如果存储过程中的逻辑可以约束返回数据的话(缩小数据集的大小、缩短网络上所花费时间,不必筛选逻辑层的数据),则应赞成这样做。

使用 SqlCommand 实例及其 ExecuteReader 方法填充强类型的业务类时,可以通过调用 NextResult 将结果集指针向前移动。图 1 显示了使用类型类填充几个 ArrayList 的示例会话。只从数据库返回您需要的数据将进一步减少服务器上的内存分配。

技巧 2 — 分页的数据访问

ASP.NET DataGrid 具有一个很好的功能:数据分页支持。在 DataGrid 中启用分页时,一次会显示固定数量的记录。另外,在 DataGrid 的底部还会显示分页 UI,以便在记录之间进行导航。该分页 UI 使您能够在所显示的数据之间向前和向后导航,并且一次显示固定数量的记录。

还有一个小小的波折。使用 DataGrid 的分页需要所有数据均与网格进行绑定。例如,您的数据层需要返回所有数据,那么 DataGrid 就会基于当前页筛选显示的所有记录。如果通过 DataGrid 进行分页时返回了 100,000 个记录,那么针对每个请求会放弃 99,975 个记录(假设每页大小为 25 个记录)。当记录的数量不断增加时,应用程序的性能就会受到影响,因为针对每个请求必须发送越来越多的数据。

要编写性能更好的分页代码,一个极佳的方式是使用存储过程。图 2 显示了针对 Northwind 数据库中的 Orders 表进行分页的一个示例存储过程。简而言之,您此时要做的只是传递页索引和页大小。然后就会计算合适的结果集,并将其返回。

在社区服务器中,我们编写了一个分页服务器控件,以完成所有的数据分页。您将会看到,我使用的就是技巧 1 中讨论的理念,从一个存储过程返回两个结果集:记录的总数和请求的数据。

返回记录的总数可能会根据所执行查询的不同而有所变化。例如,WHERE 子句可用来约束返回的数据。为了计算在分页 UI 中显示的总页数,必须了解要返回记录的总数。例如,如果总共有 1,000,000 条记录,并且要使用一个 WHERE 子句将其筛选为 1000 条记录,那么分页逻辑就需要了解记录的总数才能正确呈现分页 UI。

技巧 3 — 连接池

在 Web 应用程序和 SQL Server™ 之间设置 TCP 连接可能是一个非常消耗资源的操作。Microsoft 的开发人员到目前为止能够使用连接池已经有一段时间了,这使得他们能够重用数据库连接。他们不是针对每个请求都设置一个新的 TCP 连接,而是只在连接池中没有任何连接时才设置新连接。当连接关闭时,它会返回连接池,在其中它会保持与数据库的连接,而不是完全破坏该 TCP 连接。

当然,您需要小心是否会出现泄漏连接。当您完成使用连接时,请一定要关闭这些连接。再重复一遍:无论任何人对 Microsoft?.NET Framework 中的垃圾回收有什么评论,请一定要在完成使用连接时针对该连接显式调用 Close 或 Dispose。不要相信公共语言运行库 (CLR) 会在预先确定的时间为您清除和关闭连接。尽管 CLR 最终会破坏该类,并强制连接关闭,但是当针对对象的垃圾回收真正发生时,并不能保证。

要以最优化的方式使用连接池,需要遵守一些规则。首先打开连接,执行操作,然后关闭该连接。如果您必须如此的话,可以针对每个请求多次打开和关闭连接(最好应用技巧 1),但是不要一直将连接保持打开状态并使用各种不同的方法对其进行进出传递。第二,使用相同的连接字符串(如果使用集成身份验证的话,还要使用相同的线程标识)。如果不使用相同的连接字符串,例如根据登录的用户自定义连接字符串,那么您将无法得到连接池提供的同一个优化值。如果您使用集成身份验证,同时还要模拟大量用户,连接池的效率也会大大下降。尝试跟踪与连接池相关的任何性能问题时,.NET CLR 数据性能计数器可能非常有用。

每当应用程序连接资源时,如在另一个进程中运行的数据库,您都应该重点考虑连接该资源所花时间、发送或检索数据所花时间,以及往返的数量,从而进行优化。优化应用程序中任何种类的进程跳跃都是获得更佳性能的首要一点。

应用层包含了连接数据层、将数据转换为有意义类实例和业务流程的逻辑。例如社区服务器,您要在其中填充Forums 或 Threads集合,应用业务规则(如权限);最重要的是要在其中执行缓存逻辑。

技巧 4 — ASP.NET 缓存 API

编写应用程序代码行之前,一个首要完成的操作是设计应用层的结构,以便最大化利用 ASP.NET 缓存功能。

如果您的组件要在 ASP.NET 应用程序中运行,则只需在该应用程序项目中包括一个 System.Web.dll 引用。当您需要访问该缓存时,请使用 HttpRuntime.Cache 属性(通过 Page.Cache 和 HttpContext.Cache 也可访问这个对象)。

对于缓存数据,有几个规则。首先,如果数据可能会多次使用时,则这是使用缓存的一个很好的备选情况。第二,如果数据是通用的,而不特定于某个具体的请求或用户时,则也是使用缓存的一个很好的备选情况。如果数据是特定于用户或请求的,但是寿命较长的话,仍然可以对其进行缓存,但是这种情况可能并不经常使用。第三,一个经常被忽略的规则是,有时可能您缓存得太多。通常在一个 x86 计算机上,为了减少内存不足错误出现的机会,您会想使用不高于 800MB 的专用字节运行进程。因此缓存应该有个限度。换句话说,您可能能够重用某个计算结果,但是如果该计算采用 10 个参数的话,您可能要尝试缓存 10 个排列,这样有可能给您带来麻烦。一个要求 ASP.NET 的最常见支持是由于过度缓存引起的内存不足错误,尤其是对于大型数据集。


图 3 ASP.NET缓存


缓存有几个极佳的功能,您需要对它们有所了解。首先,缓存会实现最近最少使用的算法,使得 ASP.NET 能够在内存运行效率较低的情况下强制缓存清除 - 从缓存自动删除未使用过的项目。第二,缓存支持可以强制失效的过期依赖项。这些依赖项包括时间、密钥和文件。时间经常会用到,但是对于 ASP.NET 2.0,引入了一个功能更强的新失效类型:数据库缓存失效。它指的是当数据库中的数据发生变化时自动删除缓存中的项。有关数据库缓存失效的详细信息,请参阅 MSDN?Magazine 2004 年 7 月的 Dino Esposito Cutting Edge 专栏。要了解缓存的体系结构,请参阅图 3。

技巧 5 — 每请求缓存

在本文前面部分,我提到了经常遍历代码路径的一些小改善可能会导致较大的整体性能收益。对于这些小改善,其中有一个绝对是我的最爱,我将其称之为“每请求缓存”。

缓存 API 的设计目的是为了将数据缓存较长的一段时间,或者缓存至满足某些条件时,但每请求缓存则意味着只将数据缓存为该请求的持续时间。对于每个请求,要经常访问某个特定的代码路径,但是数据却只需提取、应用、修改或更新一次。这听起来有些理论化,那么我们来举一个具体的示例。

在社区服务器的论坛应用程序中,页面上使用的每个服务器控件都需要个性化的数据来确定使用什么外观、使用什么样式表,以及其他个性化数据。这些数据中有些可以长期缓存,但是有些数据却只针对每个请求提取一次,然后在执行该请求期间对其重用多次,如要用于控件的外观。

为了达到每请求缓存,请使用 ASP.NET HttpContext。对于每个请求,都会创建一个 HttpContext 实例,在该请求期间从 HttpContext.Current 属性的任何位置都可访问该实例。该 HttpContext 类具有一个特殊的 Items 集合属性;添加到此 Items 集合的对象和数据只在该请求持续期间内进行缓存。正如您可以使用缓存来存储经常访问的数据一样,您也可以使用 HttpContext.Items 来存储只基于每个请求使用的数据。它背后的逻辑非常简单:数据在它不存在的时候添加到 HttpContext.Items 集合,在后来的查找中,只是返回 HttpContext.Items 中的数据。

技巧 6 — 后台处理

通往代码的路径应该尽可能快速,是吗?可能有时您会觉得针对每个请求执行的或者每 n 个请求执行一次的任务所需资源非常多。发送电子邮件或者分析和验证传入数据就是这样的一些例子。

剖析 ASP.NET Forums 1.0 并重新构建组成社区服务器的内容时,我们发现添加新张贴的代码路径非常慢。每次添加新张贴时,应用程序首先需要确保没有重复的张贴,然后必须使用“坏词”筛选器分析该张贴,分析张贴的字符图释,对张贴添加标记并进行索引,请求时将张贴添加到合适的队列,验证附件,最终张贴之后,立即向所有订阅者发出电子邮件通知。很清楚,这涉及很多操作。

经研究发现,大多数时间都花在了索引逻辑和发送电子邮件上。对张贴进行索引是一个非常耗时的操作,人们发现内置的 System.Web.Mail 功能要连接 SMYP 服务器,然后连续发送电子邮件。当某个特定张贴或主题领域的订阅者数量增加时,执行 AddPost 功能所需的时间也越来越长。

并不需要针对每个请求都进行电子邮件索引。理想情况下,我们想要将此操作进行批处理,一次索引 25 个张贴或者每五分钟发送一次所有电子邮件。我们决定使用以前用于对数据缓存失效进行原型设计的代码,这个失效是用于最终进入 Visual Studio® 2005 的内容的。

System.Threading 命名空间中的 Timer 类非常有用,但是在 .NET Framework 中不是很有名,至少对于 Web 开发人员来说是这样。创建之后,这个 Timer 类将以一个可配置的间隔针对 ThreadPool 中的某个线程调用指定的回调。这就表示,您可以对代码进行设置,使其能够在没有对 ASP.NET 应用程序进行传入请求的情况下得以执行,这是后台处理的理想情况。您还可以在此后台进程中执行如索引或发送电子邮件之类的操作。

但是,这一技术有几个问题。如果应用程序域卸载,该计时器实例将停止触发其事件。另外,因为 CLR 对于每个进程的线程数量具有一个硬性标准,所以可能会出现这样的情形:服务器负载很重,其中计时器可能没有可在其基础上得以完成的线程,在某种程度上可能会造成延迟。ASP.NET 通过在进程中保留一定数量的可用线程,并且仅使用总线程的一部分用于请求处理,试图将上述情况发生的机会降到最低。但是,如果您具有很多异步操作时,这可能就是一个问题了。

这里没有足够的空间来放置该代码,但是您可以下载一个可以看懂的示例,网址是 www.rob-howard.net。请了解一下 Blackbelt TechEd 2004 演示中的幻灯片和演示。

技巧 7 — 页输出缓存和代理服务器

ASP.NET 是您的表示层(或者说应该是您的表示层);它由页、用户控件、服务器控件(HttpHandlers 和 HttpModules)以及它们生成的内容组成。如果您具有一个 ASP.NET 页,它会生成输出(HTML、XML、图像或任何其他数据),并且您针对每个请求运行此代码时,它都会生成相同的输出,那么您就拥有一个可用于页输出缓存的绝佳备选内容。

将此行内容添加页的最上端

<%@ Page OutputCache VaryByParams="none" Duration="60" %>

就可以高效地为此页生成一次输出,然后对它进行多次重用,时间最长为 60 秒,此时该页将重新执行,输出也将再一次添加到 ASP.NET 缓存。通过使用一些低级程序化 API 也可以完成此行为。对于输出缓存有几个可配置的设置,如刚刚讲到的 VaryByParams 属性。VaryByParams 刚好被请求到,但还允许您指定 HTTP GET 或 HTTP POST 参数来更改缓存项。例如,只需设置 VaryByParam="Report" 即可对 default.aspx?Report=1 或 default.aspx?Report=2 进行输出缓存。通过指定一个以分号分隔的列表,还可以指定其他参数。

很多人都不知道何时使用输出缓存,ASP.NET 页还会生成一些位于缓存服务器下游的 HTTP 标头,如 Microsoft Internet Security and Acceleration Server 或 Akamai 使用的标头。设置了 HTTP 缓存标头之后,可以在这些网络资源上对文档进行缓存,客户端请求也可在不必返回原始服务器的情况下得以满足。

因此,使用页输出缓存不会使得您的应用程序效率更高,但是它可能会减少服务器上的负载,因为下游缓存技术会缓存文档。当然,这可能只是匿名内容;一旦它成为下游之后,您就再也不会看到这些请求,并且再也无法执行身份验证以阻止对它的访问了。

技巧 8 — 运行 IIS 6.0(只要用于内核缓存)

如果您未运行 IIS 6.0 (Windows Server? 2003),那么您就错过了 Microsoft Web 服务器中的一些很好的性能增强。在技巧 7 中,我讨论了输出缓存。在 IIS 5.0 中,请求是通过 IIS 然后进入 ASP.NET 的。涉及到缓存时,ASP.NET 中的 HttpModule 会接收该请求,并返回缓存中的内容。

如果您正在使用 IIS 6.0,就会发现一个很好的小功能,称为内核缓存,它不需要对 ASP.NET 进行任何代码更改。当请求由 ASP.NET 进行输出缓存时,IIS 内核缓存会接收缓存数据的一个副本。当请求来自网络驱动程序时,内核级别的驱动程序(无上下文切换到用户模式)就会接收该请求,如果经过了缓存,则会将缓存的数据刷新到响应,然后完成执行。这就表示,当您将内核模式缓存与 IIS 和 ASP.NET 输出缓存一起使用时,就会看到令人不敢相信的性能结果。在 ASP.NET 的 Visual Studio 2005 开发过程中,我一度是负责 ASP.NET 性能的程序经理。开发人员完成具体工作,但是我要看到每天进行的所有报告。内核模式缓存结果总是最有意思的。最常见的特征是网络充满了请求/响应,而 IIS 运行时的 CPU 使用率只有大约 5%。这太令人震惊了!当然使用 IIS 6.0 还有一些其他原因,但是内核模式缓存是其中最明显的一个。

技巧 9 — 使用 Gzip 压缩

虽然使用 gzip 并不一定是服务器性能技巧(因为您可能会看到 CPU 使用率的提高),但是使用 gzip 压缩可以减少服务器发送的字节数量。这就使人们觉得页速度加快了,并且还减少了带宽的用量。根据所发送数据、可以压缩的程度以及客户端浏览器是否支持(IIS 只会向支持 gzip 压缩的客户端发送经过 gzip 压缩的内容,如 Internet Explorer 6.0 和 Firefox),您的服务器每秒可以服务于更多的请求。实际上,几乎每当您减少所返回数据的数量时,都会增加每秒请求数。

Gzip 压缩已经内置到 IIS 6.0 中,并且其性能比 IIS 5.0 中使用的 gzip 压缩要好的多,这是好消息。但不幸的是,当尝试在 IIS 6.0 中打开 gzip 压缩时,您可能无法在 IIS 的属性对话中找到该设置。IIS 小组在该服务器中置入了卓越的 gzip 功能,但是忘了包括一个用于启用该功能的管理 UI。要启用 gzip 压缩,您必须深入到 IIS 6.0 的 XML 配置设置内部(这样不会引起心脏虚弱)。顺便提一句,这归功于 OrcsWeb 的 Scott Forsyth,他帮助我提出了在 OrcsWeb 上宿主的 www.asp.net 服务器的这个问题。

本文就不讲述步骤了,请阅读 Brad Wilson 的文章,网址是 IIS6 Compression。还有一篇有关为 ASPX 启用压缩的知识库文章,网址是 Enable ASPX Compression in IIS。但是您应该注意,由于一些实施细节,IIS 6.0 中不能同时存在动态压缩和内核缓存。

技巧 10 — 服务器控件视图状态

视图状态是一个有趣的名称,用于表示在所生成页的隐藏输出字段中存储一些状态数据的 ASP.NET。当该页张贴回服务器时,服务器可以分析、验证、并将此视图状态数据应用回该页的控件树。视图状态是一个非常强大的功能,因为它允许状态与客户端一起保持,并且它不需要 cookie 或服务器内存即可保存此状态。很多 ASP.NET 服务器控件都使用视图状态来保持在与页元素进行交互期间创建的设置,例如保存对数据进行分页时显示的当前页。

然而使用视图状态也有一些缺点。首先,服务或请求页时,它都会增加页的总负载。对张贴回服务器的视图状态数据进行序列化或取消序列化时,也会发生额外的开销。最后,视图状态会增加服务器上的内存分配。

几个服务器控件有着过度使用视图状态的趋势,即使在并不需要的情况下也要使用它,其中最著名的是 DataGrid。ViewState 属性的默认行为是启用,但是如果您不需要,则可以在控件或页级别关闭。在控件内,只需将 EnableViewState 属性设置为 false,或者在页中使用下列设置即可对其进行全局设置:

<%@ Page EnableViewState="false" %>

如果您不回发页,或者总是针对每个请求重新生成页上的控件,则应该在页级别禁用视图状态。

小结

我为您讲述了一些我认为在编写高性能 ASP.NET 应用程序时有所帮助的技巧。正如我在本文前面部分提到的那样,这是一个初步指南,并不是 ASP.NET 性能的最后结果。(有关改善 ASP.NET 应用程序性能的信息,请参阅 Improving ASP.NET Performance。)只有通过自己的亲身体验才能找出解决具体性能问题的最好方法。但是,在您的旅程中,这些技巧应该会为您提供一些好的指南。在软件开发中,几乎没有绝对的东西;每个应用程序都是唯一的。

请参阅提要栏“Common Performance Myths”。

Rob Howard 是 Telligent Systems 的创始人,专门从事高性能 Web 应用程序、知识库管理和协作系统方面的工作。Rob 以前受雇于 Microsoft,他在那里帮助设计了 ASP.NET 1.0、1.1 和 2.0 的基础结构。要联系 Rob,请访问 rhoward@telligentsystems.com

16:29  |  固定链接 | 评论 (0) | 引用通告 (0) | 记录它
用C#生成Excel文件的方法和Excel.dll组件生成的方法
标题   用C#生成Excel文件的方法和Excel.dll组件生成的方法     选择自 wang8712 的 Blog 关键字   用C#生成Excel文件的方法和Excel.dll组件生成的方法 出处  

一个示例:

class AppTest
 {
  private Excel.ApplicationClass _x;
  public static void Main0()
  {
   AppTest a = new AppTest();
   a._x = new Excel.ApplicationClass();
   a._x.UserControl = false;
   for (int i = 0 ;i < 4; i++)
   {
    
    a.SaveToXls("D:\\test\\" + i + ".xls");  // 本例是在D盘下建立的test文件夹
   }
   a._x.Quit();
   System.Runtime.InteropServices.Marshal.ReleaseComObject((object) a._x);
   System.GC.Collect();
  }

  private void SaveToXls(string filename)
  {
   Excel.WorkbookClass wb = (Excel.WorkbookClass) this._x.Workbooks.Add(System.Reflection.Missing.Value);
   for(int i = 1;i <= 4; i++)
   {
    this._x.Cells[i,1]=i.ToString();
    this._x.Cells[i,2]="'bbb2";
    this._x.Cells[i,3]="'ccc3";
    this._x.Cells[i,4]="'aaa4";
   }
   
   wb.Saved = true;
   this._x.ActiveWorkbook.SaveCopyAs(filename);
  }
 }

【注:在VS.Net中运行是要添加Excel.dll组件的,Excel组件VS.Net本身是没有的,下面是生成Excel.dll的方法。】

1.要保证机器本身要安装OFFICE.

2.把[C:\Program Files\Microsoft Office\Office:默认安装路径]下的EXCEL9.OLB文件拷贝到[C:\Visual Studio.Net\SDK\v1.1\Bin:VS.Net安装路径]路径下。

3.打开Visual Studio .Net2003命令提示,运行TlbImp Excel9.olb Excel.dll ,就会在[C:\Visual Studio.Net\SDK\v1.1\Bin]下生成Excel.dll组件。

4.在项目中添加Excel.dll引用就OK了。

13:53  |  固定链接 | 评论 (0) | 引用通告 (0) | 记录它
利用 SQL Server Reporting Services 从应用程序生成用户友好的报表
http://www.microsoft.com/china/msdn/library/data/sqlserver/SQLServerReportServ.mspx 利用 SQL Server Reporting Services 从应用程序生成用户友好的报表
发布日期: 09/03/2004 | 更新日期: 09/03/2004
本文讨论: •

Reporting

设计和部署报表

使用 Reporting Services 的 Web 服务

保证报表的安全

本文使用下列技术:
SQL Server、ASP.NET、Visual Basic .NET

代码下载:
SQLServerReportingServices.exe(222KB)

*
本页内容

Reporting Services 概述 Reporting Services 概述 设计第一个报表 设计第一个报表 部署和测试 部署和测试 将报表添加到 Web 应用程序 将报表添加到 Web 应用程序 将报表嵌入到 Web 应用程序中 将报表嵌入到 Web 应用程序中 保证报表的安全 保证报表的安全 使用 Reporting Services Web 服务 使用 Reporting Services Web 服务 订阅报表 订阅报表 接下来该怎么做? 接下来该怎么做?

灵活的报表功能是大多数业务应用程序的一个要求,这些报表功能在集成到 Web 应用程序中之后用途更加广泛。利用 SQL Server® 2000 Reporting Services 的最新版本,您可以轻松地具有来自各种数据源的报表生成功能。在本文中,我将介绍使用 Visual Studio® 和 Reporting Services 来编写报表,并演示如何将报表集成到 Web 应用程序中。


Reporting Services 是基于服务器的报表生成平台,该平台构建在 .NET Framework 上并与 SQL Server 2000 集成在一起,因此您可以使用一个扩展的基于 Web 服务的 API 将丰富的报表生成功能集成到应用程序中。尽管报表服务器使用 SQL Server 作为报表的储存库,但利用 OLE DB、ODBC 或 ADO.NET 提供程序的任何数据源都可以用来为报表提供数据,这就使得 Reporting Services 成为在各种企业环境中生成报表的极佳工具。


Reporting Services 是作为 SQL Server 2000 的一部分而授权的,因此如果您有已授权的 SQL Server 的副本,则可以在同一服务器上运行 Reporting Services 而无需支付额外的许可证费用。如果您决定在独立的、尚未经 SQL Server 授权的计算机(例如,Web 服务器)上运行 Reporting Services,您将需要购买额外的 SQL Server 许可证。


要开始使用 Reporting Services,您可以下载位于 SQL Server 2000 Reporting Services 的 120 天试用版软件,它还包括有关如何获得该产品的安装媒体的说明。在安装该产品时,请确保选中包括示例报表的选项,因为我要将这些报表集成到本文的 Web 应用程序中。



Reporting Services 概述

Reporting Services 报表设计使用新的 Visual Studio .NET 2003 报表设计器,在安装有 Visual Studio 的同一计算机上安装了 Reporting Services 后,该设计器就可以使用。设计器以新的基于 XML 报表定义语言 (RDL) 输出报表布局和数据访问的说明,而 RDL 文件会发布到报表服务器。如果您希望为用户提供创作功能,也可以使用第三方的报表设计器。


管理员可以使用基于 Web 的报表管理器来管理已发布的报表,并且可以执行一些任务,例如,保证某些用户组报表的安全或更改已部署报表的数据源连接字符串。用户也可以使用报表管理器来浏览和查看报表;但是很多公司倾向于使用 URL 请求或 Reporting Services Web 服务,将报表查看直接集成到它们现有的内部 Web 站点或应用程序中。



USissues0408SQLServerReportingServicesfig01

1 Reporting Services 体系结构



可以将报表参数化,这样用户可以在查看报表时从选择列表中进行挑选;它们还可以导出为多种格式,例如,Microsoft®Excel、PDF 和 XML。尽管实时报表提供最新的数据,也可以将报表缓存一段时间以提高性能并减少数据源上的负载。对于商务智能应用程序,报表可以访问 Analysis Services OLAP 多维数据集,而且 Reporting Services 甚至可以导入现有的 Microsoft Access 报表,尽管由于这两种技术间存在差异,它并不能支持所有的 Access 功能。有关 Reporting Services 体系结构的详细情况,请参见 1



设计第一个报表

尽管我介绍的重点在于将报表集成到应用程序中而不是创作报表,但熟悉 Reporting Services 的最好办法就是立即着手创建一个新的报表项目。为此,请打开 Visual Studio 并使用 Report Project Wizard 创建一个新的项目,该向导位于 Business Intelligence Projects 部分。为您的项目指定一个名称,单击“OK”,然后将显示报表向导对话框。在您选择数据源的这一步骤中,单击“Edit”按钮,以便指定连接到您数据库服务器上的 AdventureWorks2000,单击“OK”,然后选中使其成为共享数据源的选项。


单击“Next”进入到“Design the Query”步骤,指定简单的 SQL 语句(例如,“SELECT * FROM Product”),然后单击“Next”。Reporting Services 支持显示一组固定列的标准表格式 (Tabular) 报表,也支持使用带有动态列的交叉表样式视图的矩阵式 (Matrix) 报表,因此为该报表选择 Tabular,然后单击“Next”。


将某些列(例如,ProductID 和 Name)拖到 Details 部分并单击“Next”。选择默认的 Bold 样式,单击“Next”,然后指定您的报表服务器的虚拟目录的 URL,例如,http://MyServer/ReportServer。现在,单击“Next”。最后一步,您可以为您的第一个报表指定名称,例如,Products Report;然后单击“Finish”。


该向导将会创建该项目,同时还创建一个可以由多个报表共享 AdventureWorks2000 数据源,以及一个在设计器中打开的有关 Products 数据的报表(请参见 2)。默认情况下,显示使您可以修改报表设计的 Layout 选项卡。



USissues0408SQLServerReportingServicesfig02

2 Visual Studio 报表设计器




Data 选项卡用于指定报表的查询,包括设置参数以便在执行报表时提示用户提供值。单击“Preview”选项卡来测试报表显示给用户的方式。


现在,您可以将附加的报表添加到该项目,或者通过添加附加列、将数据分组、添加汇总、甚至是添加能够显示来自其他查询的数据的附加表来修改 Products 报表。与很多报表设计器不同,Reporting Services 使用带外设计,这使得它可以轻松地创建将各种源的数据集中在同一个位置的报表。


您刚刚创建的报表项目由当前驻留在您的开发计算机上的一组文件组成。为了将这些报表发布给用户,您需要将该项目部署到报表服务器。



部署和测试

在部署报表项目之前,需要指定将项目部署到哪一个服务器。如果您最初使用该向导创建了报表项目,那么您可能已经指定了目标服务器。


要检查该设置或指定一个新服务器,请选择“Project | Properties”来显示 Project Properties 对话框。进行该设置使您可以指定项目的文件夹名和 Reporting Services Web 服务的 URL。在菜单上选择“Build | Deploy Solution”将构建报表项目,然后将其部署到服务器。


在部署报表项目后,您就可以进行测试了。已部署到服务器的每个报表都具有唯一的路径,该路径可以用于在浏览器中显示报表。例如,要显示您创建的第一个报表,请打开浏览器并导航到 http://MyServer/ReportServer?/Report+Project1/Products+Report。这会以 HTML 格式呈现报表并在页面的顶端显示一个工具栏,以便使您可以对报表进行翻页、更改缩放比例,甚至以各种其他格式(例如,PDF)查看报表。


您还可以通过导航到服务器上的以下 URL,来浏览已部署到服务器的所有报表项目的列表:http://MyServer/ReportServer。报表服务器提供使您可以查看已部署的报表类型和查看报表的最小的用户界面。


报表管理器是一个更加友好、功能更丰富的工具,可以通过简单地导航到本地服务器上的以下 URL 来访问该工具:http://MyServer/Reports

将报表添加到 Web 应用程序

既然您知道如何设计、部署和测试报表,就让我们将这些报表集成到应用程序中吧!很多 Web 应用程序都包括针对每个用户的自定义主页,通常称为“仪表板”。这个页面通常提供针对用户量身定做的摘要式信息并作为跳转点来访问应用程序的其他功能。


我将使用 Reporting Services 附带的示例报表构建一个仪表板来演示报表集成,如图 3 所示。如果您在安装过程中安装了示例报表,您可以打开该报表项目(安装到 \Reporting Services\Samples\Reports)并将其部署到测试服务器。您可以使用报表管理器工具通过浏览到 SampleReports 文件夹来测试示例报表。



USissues0408SQLServerReportingServicesfig03

3 仪表板 Web 应用程序




为用户提供从应用程序访问报表的权限的最简单方法就是使用超级链接。如您已经看到的那样,每个报表都具有唯一的、用于在浏览器中显示报表的路径。对于仪表板应用程序,报表应该在单独的窗口中打开,因此我将为该超级链接指定一个目标。到目前为止,这是非常简单的,但是对于具有参数报表的报表(例如,Employee Sales Summary 报表)来说,情况又如何呢?要真正地将报表集成到应用程序中,您通常需要为参数指定某些值,以便用户不会被经常提示。


Reporting Services 使这项任务非常简单:报表参数的值可以指定为 URL 的一部分。例如,Employee Sales Summary 报表具有 ReportYear、ReportMonth 和 EmpID 参数,因此显示编号为 24 的员工在 11 月份的销售额的超级链接可以是:

<a href="http://MyServer/ReportServer?/SampleReports/Employee Sales Summary&ReportMonth=11&EmpID=24" target="_blank">November Sales Summary </a>

不具有在 URL 中指定的值的参数将使用在报表设计器中设置的默认值,因此刚才展示的示例销售汇总报表使用的 ReportYear 值是 2003。Reporting Services 还有一组用于控制报表显示方式的内置参数。这些参数通过加上前缀“rs:”来区别您自己的报表参数。


其中最有用的一个参数就是 rs:Format 参数,它用于指定呈现报表的格式。这使您可以包括不需要显示在 HTML 中的报表,还可以使用诸如 PDF 甚至是 XML 的格式。其他常用参数是 rs:Command,指示出要应用到您所指定的路径的操作。例如,rs:Command=Render 将呈现一个报表,而 rs:Command=ListChildren 将列出文件夹中所有项。


如果您没有指定命令,Reporting Services 将查看您指定的路径并计算出要采取的适当操作,例如,呈现报表。图 4 显示了 rs:parameters 的列表。
另一组内置参数控制各种输出格式的行为并使用 rc:prefix。每个报表格式都具有其自己特定的一组参数。例如,要以没有标题行的逗号分隔值 (CSV) 格式呈现报表,您应该使用 rc:NoHeader 参数:

<a href="http://MyServer/ReportServer?/SampleReports/Company Sales &rs:Command=Render&rs:Format=CSV&rc:NoHeader=true">Company Sales </a>

HTML 格式具有非常多的 rc:parameters,在将报表集成到 Web 应用程序中时为您提供很多的灵活性。如果应用程序将报表参数选择传递到报表,那么您可能要通过将 rc:Parameters 设置为 false 来避免提示用户,甚至可以通过将 rc:Toolbar 设置为 false 来关闭整个工具栏。有关常用 HTML rc:parameters 的列表的详细信息,请参见图 5


如果您的 Web 应用程序使用框架来显示报表(比如说,在左侧窗格中显示报表列表,在右侧窗格中显示报表内容),您将需要使用 rc:LinkTarget 参数来指定内容框架的名称。否则,当用户点击报表中的任意嵌入链接时,浏览器就会使用整个窗口来重新显示报表,而不是保留导航窗格和内容窗格的框架。


您可以使用我介绍过的 URL 参数来添加“快速启动”区域,该区域包含使用户可以打开常用报表的超级链接的列表(请参见图 3)。每个超级链接将其目标设置为 _blank 以在独立的浏览器窗口中打开报表,并使用诸如 rs:Format=EXCEL 这样的参数来控制报表在浏览器中的显示方式。


将报表嵌入到 Web 应用程序中

如果在用户查看报表时,不再使用弹出新的浏览器窗口,您可能希望实际地将报表嵌入到自己的 Web 页中。最简单的方法就是在 Web 页上使用 IFRAME,并使用我介绍过的基于 URL 的相同技术来设置 SRC 属性。如果您使用这种技术,切记要将 rc:LinkTarget 参数设置为 IFRAME 的名称,以避免当用户单击报表的链接时在框架外弹出任意一个框架。


如果您以前设计过 ASP.NET 的服务器控件,您可能会认为 IFRAME 技术并不十分完美,因为它要求 Web 开发人员理解 Reporting Services 的 URL 参数语法才能构建适当的 SRC 字符串。构建一个封装有所有参数并使开发人员可以简单地设置诸如 ReportPath 和 Zoom 之类的参数的服务器控件难道不是一个更好的想法吗?


Reporting Services 非常方便地附带了名为 ReportViewer 的示例(安装到 \Reporting Services\Samples\Applications\ReportViewer 目录),它可以实现这个想法。该控件封装了 IFRAME 和 URL 访问参数逻辑,并提供了一个更简单的用于将报表嵌入到应用程序中的方法。要使用该控件,请打开并构建 ReportViewer 解决方案,然后通过浏览到 ReportViewer 解决方案的 bin 目录中的 ReportViewer.dll,切换到您的 Web 应用程序并将该控件添加到工具箱中。
您可以使用该控件将嵌入报表添加到仪表板应用程序中。将控件添加到 Web 页,设置高度和宽度属性并添加以下代码以便在页面中显示一个报表:

Private Sub Page_Load(ByVal sender As System.Object, ByVal _ e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then ReportViewer1.ServerUrl = "http://MyServer/ReportServer" ReportViewer1.ReportPath = "/SampleReports/Sales Order" & _ "Detail&SalesOrderNumber=so8153" ReportViewer1.Toolbar = ReportViewer.multiState.False ReportViewer1.Zoom = "75" End If End Sub
保证报表的安全

Reporting Services 安全性使用熟悉的基于角色的模型。用户和组可以分配给诸如 System Administrator 或 Browser 之类的角色,特定角色的所有成员都将允许执行为该角色定义的操作。


报表管理器用于执行与安全性相关的任务,例如,分配用户角色或修改项(例如,报表)的权限等。默认情况下,Administrators Windows 组同时是所有文件夹的 System Administrator 和 Content Manager 的成员。为了扩展报表访问到其他用户,用户帐户或组必须添加到诸如 Browser 的角色中。这通常是在文件夹级设置的,同时也可以针对单独的报表进行重写。


除了保护对报表的访问外,管理员还必须决定如何将报表连接到源数据库。可以配置数据源使用集成的安全性,以便可以使用当前的用户帐户进行数据库连接。此外,还可以定义 Windows? 或 SQL Server 用户名和密码,并将其安全地存储在报表服务器数据库中。这种方法要求报表运行在无用户交互(例如,订阅所涉及的交互)的情况下。


使用 Reporting Services Web 服务

到目前为止,我一直使用的 URL 访问方法提供了一种快速便捷的方式来查看报表并指定参数,但是它不能提供对所有可用的报表管理功能的访问。Reporting Services 还附带了一个具有丰富功能的 API,为您提供通过 Web 服务对高级功能的访问。例如,该 Web 服务包括用于管理报表的数据源信息的方法或者枚举某个文件夹中所有项的方法。


我将使用 Web 服务将报表的动态列表添加到仪表板应用程序中。为此,您将需要使用 Add Web Reference 将一个 Web 服务引用添加到应用程序中。指定到 Web 服务的路径,如下所示:

http://MyServer/ReportServer/ReportService.asmx

在添加引用后,您可以创建 ReportingService 代理类的一个实例,并调用该服务上的一个方法,就像我在此处完成的一样:

Private Sub LoadSampleReports() Dim rs As New ReportingService rs.Credentials = System.Net.CredentialCache.DefaultCredentials Dim item As CatalogItem For Each item In rs.ListChildren("/SampleReports", False) If item.Type = ItemTypeEnum.Report Then DropDownList1.Items.Add(New ListItem(item.Name, item.Path)) End If Next End Sub

如果您正在一个 Intranet 环境中部署应用程序,您要通过将 添加到应用程序的 web.config 文件中来启用模拟。您还要将客户端登录凭据传递到该服务,如前面的示例所示。


ListChildren 方法返回 CatalogItem 对象的数组,该数组表示用户有权查看的项,包括报表、文件夹和数据源。Path 属性提供了到项的唯一路径,例如,/SampleReports/Sales Order Detail,Type 属性使您可以区别报表和其他类型的项。


既然我已经将报表名称加载到了下拉列表中,我必须决定如何在仪表板应用程序内显示报表。我决定扩展我早期编写的 ReportViewer 代码并且只要用户选择某个报表并点击“Go”按钮就更改 ReportPath 属性。您还可以使用 IFRAME 并将 SRC 设置为适当的 URL(并为报表的名称加上前缀“http://MyServer/ReportServer?”)。


除了报表管理功能外,Web 服务提供的其他主要功能就是报表呈现。大多数应用程序(包括报表管理器应用程序)都使用 Web 服务功能来列出报表或更改设置,并使用我介绍过的 URL 方法来显示报表。但是,如果您想要完全控制报表内容在应用程序中处理的方式,可以使用 ReportingService 类的 Render 方法。该方法返回一个可以在以后保存到输出文件的字节数组;取决于请求的格式,输出文件可以是图形文件或 Excel 电子表格。如果您要以 HTML 格式呈现报表,可能会更加复杂一些,因为您将需要使用 RenderStream 方法来分别呈现图像或其他资源。


尽管本文的讨论重点在于 Web 应用程序,您也可以使用相同的技术将报表生成功能添加到基于 Windows 的应用程序中。最简单的方法仍是使用 URL 访问,或者从应用程序启动用户的浏览器,或者包括 Microsoft WebBrowser ActiveX? 控件并使用 Navigate 方法来加载适当的 URL。当您想要更好地控制某些操作(例如,在图片框中显示结果或将文件直接保存到磁盘的操作)时,Web 服务的 Render 方法是一个很好的备选方法。


信息工作者通常必须处理两种类型的报表:当他们需要收集信息时他们想要获得的类型以及每周都出现在他们桌面上或他们电子邮件中的类型。至此,我已重点阐述了用户获取他们自己的报表的过程,但 Reporting Services 还包括一种非常强大的称为订阅的功能,它使您可以将报表“推”给客户。


订阅报表

Reporting Services 订阅功能让用户按照计划接收报表。报表通常会通过电子邮件发送到用户,但 Reporting Services 同时还让您将报表生成到一个文件共享甚至可以编写自己的提交扩展。这使您可以轻松地完成分发任务,例如,向每个销售代表以电子邮件方式发送每周销售统计报表,或者设置每月财政报表在每个月的最后一天运行,并将其作为一个 PDF 写出到公司文件共享。


在设置订阅前,您需要考虑报表将如何连接到数据库以检索其数据。因此,订阅的报表将不会由用户直接执行,您无法为数据源使用集成的安全性,但在报表运行时必须指定要使用的用户名和密码。如果您试图为使用集成安全性的报表设置订阅,Reporting Services 将会返回一个错误。


要更改示例报表的数据源,请打开“Report Manager | Sample Reports”,然后单击 AdventureWorks 数据源。该数据源由所有示例报表共享,因此对它所做的任何更改将会影响所有报表。选中“Credentials stored securely in the report server”选项,指定一个具有访问 AdventureWorks 数据库权限的帐户的有效域用户名和密码,选中“Use as Windows credentials when connecting to the data source option”,然后单击“Apply”按钮。请注意,这种更改意味着指定的帐户将始终用于连接该数据库,不管实际上是哪个用户运行该报表。


因此,您已经准备好深入研究并查看提供对订阅访问的 Web 服务 API。使用 Web 服务的这一部分比使用 List 或 Render 方法要多一些挑战,主要原因在于可用选项的数量。我将要使用的方法是 CreateSubscription,它也属于 ReportingService 类。有关该方法的参数的详细说明,请参阅 ReportingService 类,但最基本的是您需要指定要订阅的报表、生成计划(例如,每周一早晨或每月的最后一个周五)、电子邮件选项(包括电子邮件地址)以及所有报表参数值。


我已经将名为 EmailSubscriber 的实用工具类包括在本文的示例代码中,该示例代码摘要了某些复杂的内容。仪表板应用程序使用该类允许用户指定他们的电子邮件地址并使 Employee Sales Summary 报表通过电子邮件每周一发送给他们。在产品应用程序中,您可以调整每个用户的报表,例如,当您创建订阅时,可以根据员工的 Windows 用户帐户检索他们的员工 ID,然后适当地设置报表参数。


如果您不希望等到下周一才测试您的订阅,它有助于您了解有关 Reporting Services 如何处理订阅计划的更多信息。当您创建一个订阅时,Reporting Services 会创建一个根据要求的计划执行的 SQL Server 代理作业。您可以通过打开 Enterprise Manager,展开 Management、SQL Server Agent 和 Jobs 文件夹来查看这个作业。该订阅作业将具有“Report Server”类别,并以由 Reporting Services 用来跟踪该作业的 GUID 命名。右键单击该作业并选择 Start Job,如果您已正确地设置了您报表的方方面面,您的报表将会发送电子邮件给您。


接下来该怎么做?

您可以用来向应用程序中添加重要报表生成功能的两个其他 Reporting Services 功能包括 Data Driven Subscriptions 和 Snapshots,前者使您可以设置用户的邮件列表并以电子邮件形式将特定参数化的报表发送给他们,后者是在计划点的报表视图,同时还可以提供报表的历史视图。Reporting Services 是使用模块化的体系结构构建的,如果您需要更强大的功能,它可以使您使用自己喜好的、以 .NET 为目标的语言来添加功能强大的新扩展。

John C. Hancock 是 Microsoft 的高级顾问,致力于商务智能和 .NET 开发。他现在定居在多伦多,要联系他,请访问 http://www.johnchancock.net

摘自 2004 年 8 月刊的 MSDN Magazine
您可以在本地报刊亭购买或者最好 订阅

posted on 2005-12-08 21:18 天外飞仙 阅读(1021) 评论(0)  编辑  收藏 所属分类: .net

只有注册用户登录后才能发表评论。


网站导航: