[转载]IIS日志分析工具AWStats配置与应用详解(上)

mikel阅读(1242)

[转载]IIS日志分析工具AWStats配置与应用详解(上) – Chikoo’s Blog.

AWStats是sourceforge.net上很有名的Web/Mail/FTP服务器日志文件分析工具。
安装配置步骤(适用于分析IIS 日志文件)
1、下载AWStats, 下载地址:
http://sourceforge.net/projects/awstats/

2、由于AWStats是Pertl写的,所以要下载Perl 解释器, 下载地址:
http://activestate.com/Products/ActivePerl/

3、安装Perl 解释器ActivePerl
4、安装AWStats(这里假设安装在C:\Program Files), 出现命令提示时,第一次输入none, 第二次输入你的主机的域名
5、配置IIS日志
5.1 活动日志格式选用默认的“W3C扩充扩展日志文件格式”
5.2 点击“属性”,再选择“扩展属性”,选中下列项目:
date
time
c-ip
cs-username
cs-method
cs-uri-stem
cs-uri-query
sc-status
sc-bytes
cs-version
cs(User-Agent)
cs(Referer)
其他都不要选中。
6、建立虚拟目录cgi-bin,映射到C:\Program Files\AWStats\wwwroot\cgi-bin。
建立虚拟目录ico, 映射到C:\Program Files\AWStats\wwwroot\icon
7、修改C:\Program Files\AWStats\wwwroot\cgi-bin中的相应的配置文件:awstats.myvirtualhostname.conf(myvirtualhostname为 你第4步中输入的域名, 比如
www.cnblogs.com
)
。修改下列项目:

程序代码 程序代码
LogFile=”C:\WINDOWS\System32\LogFiles\W3SVC1\ex%YY-0%MM-0%DD-0.log”
‘ 以上为日志文件存储目录,请务必检查并输入正确路径,否则将会报错

LogFormat=”date time c-ip cs-username cs-method cs-uri-stem cs-uri-query sc-status sc-bytes cs-version cs(User-Agent) cs(Referer)”
‘以上各项与日志文件Fields字段相关,IIS5与II6 的记录格式存在细微差别,且AWStats新版设置与旧版设置也存在部分区别。新版无须指定字段,只需按配置脚本中的帮助说明设置”0″或”1″即可。上 述实例为IIS 5的设置方法,下述实例为IIS 6的设置方法:
LogFormat=”date time cs-method cs-uri-stem cs-uri-query cs-username c-ip cs-version cs(User-Agent) cs(Referer) sc-status sc-bytes”

AllowToUpdateStatsFromBrowser=1
‘ 以上设置为是否允许在AWStats分析结果页面中出现”更新信息”

LoadPlugin=”timezone +8″

8、 重启IIS, 删除C:\WINDOWS\System32\LogFiles\W3SVC1下的所有日志文件
9、使配置生效: awstats.pl -config=myvirtualhostname -update
10、配置完成,通过Web访问日志http://www.myserver.mydomain/cgi-bin/awstats.pl?config=myvirtualhostname, 点击“立即更新”。
页面截图:

11、通过任务计划自动更新:
创建批处理文件AwstatsUpate.bat,内容为 :

程序代码 程序代码
c:
cd C:\Program Files\AWStats\wwwroot\cgi-bin
awstats.pl -config=www.cnblogs.com -update

在任务计划调度运行该批处理文件。

更详细的步骤,请查看帮助文件 C:\Program Files\AWStats\docs\awstats_setup.html
参考文章:
1、http://briandesmond.com/blog/archive/2003/09/08/176.aspx
2、http://www.cnblogs.com/Files/dudu/InstallingAWStatsOnIIS6.rar(pdf 文件)

引用内容 引用内容
以下为几点需要注意的问 题:
·为确保首次配置可以被正确执行,尽可能为相关目录降低安全级别,待调试正常后再更改目录安全级别
a)Active Perl 目录安全添加账户EveryOne>>完全控制
b)IIS日志存储 目录安全添加账户EveryOne>>完全控制
c)AWStats 安装目录中 \wwwroot\cgi-bin\ 目录安全添加账户EveryOne>>完全控制

·如何将服务器中IIS日 志文件迁移至本地应用AWStats对其进行分析?
只需将服务器IIS日志文件迁移至本地IIS日志相同存储路径即可,同时修改 awstats.myvirtualhostname.conf 配置文件中的IIS日志对应文件名称,并利用
awstats.pl -config=myvirtualhostname -update 该命令生成新的日志记录,打开浏览器访问http://www.myserver.mydomain/cgi-bin/awstats.pl?config=myvirtualhostname即 可观测到服务器迁移至本地的IIS日志分析报告;

·如何分析以前的IIS日志信息?如何分析指定日期的IIS日志信息?
操作方法 与”如何将服务器中IIS日志文件迁移至本地应用AWStats对其进行分析?”相同,只需对配置文件进行修改,指定要分析的文件名称,并生成报告即可;

· 为什么我的没有数据呢??一直显示从未更新!
类似情况在我的实际应用中也遇到过,通过与dudu沟通得知,可能原因为日志文件过大(1GB或更 高)而导致类似现象发生,解决方法就是利用”awstats.pl -config=myvirtualhostname -update”该命令生成报告,并通过IE浏览分析内容;

·执行awstats.pl -config=myvirtualhostname -update报错,提示信息如下:
Error: Couldn’t open server log file “h:\WINDOWS\System32\LogFiles\W3SVC1\ex051106.log” : Permission denied … …
出现该提示信息的原因在于没有正确设置IIS日志存储路径,在IIS管理器日志存储设置中指 明存储路径后,在该目录中还会生成一个新的目录,通常在观察路径与设置路径时易被忽视,请仔细检查配置文件中的IIS日志存储路径;

·AWStats 分析报告存放在什么位置?
详见下节,AWStats配置详解

·AWStats安全验证解决方案
详见下节,AWStats 配置详解

·IIS中多站点目录日志分析如何设置
详见下节,AWStats配置详解

[转载]【翻译】ASP.NET操作WMI——用代码向IIS6添加站点、虚拟目录和主机头

mikel阅读(1189)

[转载]【翻译】ASP.NET操作WMI——用代码向IIS6添加站点、虚拟目录和主机头 | 天机不可泄漏.

原文:WMI Functions from ASP.NET
作者:JeroenMX

前言

本文介绍了ASP.NET如何通过WMI创建站点、添加虚拟目录和添加主机头。并且已在Windows Server 2003及IIS6的环境下测试通过。

这玩意儿花了老子3天时间才搞定,用了几个小时写代码,而且当中还花了不少时间解决Win32: Access denied error的问题。当然我要指出的是,无论NETWORK SERVER帐户还是IUSR_<servername>帐户都不要设置过大的权限。对于WMI和IIS metabase的安全机理,我还是一无所知的。我只不过解决问题而已。

看代码

首先要从Internet信息服务(IIS)管理器中获取网站标识符,点击“网站”根节点,右侧“标识符”显示的就是网站的ID。默认网站的标识符 通常是1。

获取网站标识的功能,我们要用到一个namespace,代码如下:

1 using System.Management;

下文所有’ServerName’都表示你的服务器名称,或者如果你的代码是本地运行的,也可以用一个点来表示。

创建一个站点,你会用到如下函数。这个函数返回新网站的ID,这样你可以进一步对这个网站进行操作。

01 public static string CreateWebsite(string serverName, string appPoolName, string ip, string pathToRoot, string hostName, string domainName, int port)
02 {
03 ConnectionOptions options = new ConnectionOptions();
04 options.Authentication = AuthenticationLevel.Connect;
05 options.EnablePrivileges = true;
06 options.Impersonation = ImpersonationLevel.Impersonate;
07 ManagementScope scope = new ManagementScope(string.Format(@\\{0}\root\MicrosoftIISv2, serverName), options);
08 scope.Connect();
09 ManagementObject oW3SVC = new ManagementObject(scope,
10 new ManagementPath(@"IIsWebService='W3SVC'"), null);
11
12 ManagementBaSEObject[] serverBindings = new ManagementBaSEObject[1];
13 serverBindings[0] = CreateServerBinding(scope, string.Format("{0}.{1}", hostName, domainName), ip, port);
14 ManagementBaSEObject inputParameters = oW3SVC.GetMethodParameters("CreateNewSite");
15 inputParameters["ServerComment"] = string.Format("{0}.{1}", hostName, domainName);
16 inputParameters["ServerBindings"] = serverBindings;
17 inputParameters["PathOfRootVirtualDir"] = pathToRoot;
18 ManagementBaseObject outParameter = oW3SVC.InvokeMethod("CreateNewSite", inputParameters, null);
19
20 string siteId = Convert.ToString(outParameter.Properties["ReturnValue"].Value).Replace("IIsWebServer='W3SVC/", "").Replace("'", "");
21 ManagementObject oWebVirtDir = new ManagementObject(scope,new ManagementPath(string.Format(@"IIsWebVirtualDirSetting.Name='W3SVC/{0}/root'", siteId)), null);
22 oWebVirtDir.Properties["AppFriendlyName"].Value = string.Format("{0}.{1}", hostName, domainName);
23 oWebVirtDir.Properties["AccessRead"].Value = true;
24 oWebVirtDir.Properties["AuthFlags"].Value = 5; // Integrated Windows Auth.
25 oWebVirtDir.Properties["AccessScript"].Value = true;
26 oWebVirtDir.Properties["AuthAnonymous"].Value = true;
27 oWebVirtDir.Properties["AppPoolId"].Value = appPoolName;
28 oWebVirtDir.Put();
29
30 ManagementObject site = new ManagementObject(scope, new ManagementPath(Convert.ToString(outParameter.Properties["ReturnValue"].Value)), null);
31 site.InvokeMethod("Start", null);
32 return siteId;
33 }

创建一个虚拟目录:

01 public static void AddVirtualFolder(string serverName, string websiteId,string name, string path){
02 ManagementScope scope = new ManagementScope(string.Format(@"\\{0}\root\MicrosoftIISV2", serverName));
03 scope.Connect();
04
05 string siteName = string.Format("W3SVC/{0}/Root/{1}", websiteId, name);
06
07 ManagementClass mc = new ManagementClass(scope,new ManagementPath("IIsWebVirtualDirSetting"), null);
08 ManagementObject oWebVirtDir = mc.CreateInstance();
09
10 oWebVirtDir.Properties["Name"].Value = siteName;
11 oWebVirtDir.Properties["Path"].Value = path;
12 oWebVirtDir.Properties["AuthFlags"].Value = 5; // Integrated Windows Auth.
13 oWebVirtDir.Properties["EnableDefaultDoc"].Value = true;
14 // date, time, size, extension, longdate ;
15 oWebVirtDir.Properties["DirBrowseFlags"].Value = 0x4000003E;
16 oWebVirtDir.Properties["AccessFlags"].Value = 513; // read script
17 oWebVirtDir.Put();
18
19 ManagementObject mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebVirtualDir='" + siteName + "'"), null);
20 ManagementBaseObject inputParameters = mo.GetMethodParameters("AppCreate2");
21 inputParameters["AppMode"] = 2;
22 mo.InvokeMethod("AppCreate2", inputParameters, null);
23 mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebVirtualDirSetting='" + siteName + "'"), null);
24 mo.Properties["AppFriendlyName"].Value = name;
25 mo.Put();
26 }

给网站添加一个主机头:

01 public static void AddHostHeader(string serverName, string hostHeader, string ip, int port, string websiteID){
02 ManagementScope scope = new ManagementScope(string.Format(@"\\{0}\root\MicrosoftIISV2", serverName));
03 scope.Connect();
04
05 string siteName = string.Format("'W3SVC/{0}'", websiteID);
06
07 ManagementObject mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebServerSetting=" + siteName), null);
08 ManagementBaseObject[] websiteBindings = (ManagementBaseObject[])mo.Properties["ServerBindings"].Value;
09
10 ManagementObject mco = CreateServerBinding(scope, hostHeader, ip, port);
11
12 ManagementBaseObject[] newWebsiteBindings = new ManagementBaseObject[websiteBindings.Length+1];
13 websiteBindings.CopyTo(newWebsiteBindings, 0);
14 newWebsiteBindings[newWebsiteBindings.Length - 1] = mco;
15
16 mo.Properties["ServerBindings"].Value = newWebsiteBindings;
17 mo.Put();
18 }

最后别忘了这个函数,它可以为网站绑定一个网络标识。

01 </pre>
02
03 <pre>private static ManagementObject CreateServerBinding(ManagementScope scope,string hostName, string ip, int port)
04 {
05 ManagementClass mc = new ManagementClass(scope, new ManagementPath("ServerBinding"), null);
06 ManagementObject mco = mc.CreateInstance();
07
08 mco.Properties["Hostname"].Value = hostName;
09 mco.Properties["IP"].Value = ip;
10 mco.Properties["Port"].Value = port;
11 mco.Put();
12
13 return mco;
14 }

注意的几点

安全。如果之用上面的那坨代码还不行。我千方百计想让其运行,但貌似忽视了2件事情。访问WMI和IIS metabase。 ASP.NET在Windows Server 2003和IIS6.0上运行默认使用的是NETWORK SERVICE帐户。但是,我们还是要使用客户端模拟。 所以在Web.config中添加下面一个配置:

1 <identity impersonate="true" />

使用了这个配置,IUSR_<servername>会使用客户端模拟的方式去访问IIS metabase。在后面的文章里,我就用IUSR_来表示这个帐户。不要忘记,在IUSR_后面加上你的服务器名称才是这个帐户的名字。

WMI权限设置

  • 控制面板 –> 管理工具 –> 计算机管理 –> 服务和应用程序。
  • 右键WMI控制,点击“属性”。
  • 选择“安全”选项卡。
  • 展开Root树
  • 点击MicrosoftIISv2。
  • 点击“安全设置”。
  • 点击“高级”。
  • 双击IUSR_(如果“组或用户名称”里面没有的话,就把它添加进去)
  • 把IUSR_ “应用到”设置成“这个名称控件和子名称空间”
  • “允许”所有权限。
  • 所有窗口都点击“确定”。

IIS metabase权限设置

  • 下载并安装IIS6 Resource Kit
  • 运行MetaBase Explorer (在开始菜单的IIS Resource Kit中可以找到)。
  • 展开树形目录,右键第一个或第二个节点并且选择“Permissions”。
  • 如果提示你“The current key inherits its security permissions from the key /”,点击“是”。
  • 选择“IIS_IUSRS”,如果没有的话,把它添加进去。
  • 选择“Full Control”。
  • 所有窗口都点击“确定”。

有了足够的权限就能运行了。

如果有高人能对这个方法谈谈自己的感想,并且能指出更好的配置IIS和WMI的方法,那就更赞了。记得之前,我都是自己捣鼓出来的,所以我不知道这 个方法是不是最佳的方案。

如果你在运行代码的时候碰到任何问题,我愿意效劳。

译者注:

网上能搜索到的还有一种方法:使用DirectoryEntry方法,方法差不多,关键是要把权限设置好。

[转载]淘宝API读取实体类的属性的备注作为表头的应用

mikel阅读(1171)

[转载]读取实体类的属性的备注作为表头的应用 – wuhuacong(伍华聪)的专栏 – 博客园.

在做淘宝API的学习过程中,发现一个API的封装类库Top4NET对操作封装的比较好,试用了一下,效果非常不错,另外由于淘宝的API对象参 数很多,多数是英文的,另一方面Top4NET的实体类对象备注信息很详细,因此可以考虑吧实体类的属性描述信息作为列表的表头说明使用。

我们看到他的Domain里面的实体类信息描述很详细,代码如下所示:

代码

/// <summary>
/// 商品结 构
/// </summary>
[Serializable]
[JsonObject]
[XmlRoot(
item)]
public class Item : BaSEObject
{
/// <summary>
///<%

[转载]网站测试自动化系统—在测试代码中硬编码测试数据

mikel阅读(1035)

[转载]网站测试自动化系统—在测试代码中硬编码测试数据 – Killmyday – 博客园.

在前面的文章数据驱动测试里,讲到了将测试数据以及表现测试步骤的代码分开的技术。从测试的角度来看,固然希望能够覆盖的测 试场景越多越好,但是在设计和编写自动化测试代码的时候,却又可以事先设计好一些固定的测试数据简化自动化测试代码的编写工作。

之所以要这样做 (按照编程的术语讲是硬编码),是因为按照等价类划分,固定的测试数据一般都已经被其他测试用例覆盖了。请考虑下面这个例子,假设你要测试一个博客网站 (例如博客园)的文章评论功能,例如测试禁用一篇文章的评论功能,或者是测试文章作者删除评论的功能。按照正常的流程,肯定是需要先编码发布一篇文章,然 后再编码指定的评论功能测试用例。这样的流程有以下几个缺点:

1. 需要冗余的编码,因为每个评论测试用例的代码都要包含发布文章的步骤,在编程里面,我们都是极力推 荐,什么只要代码在不同的地方重复两次,就要考虑是否将它封装成一个函数之类的理念。这种包含冗余编码的方式是我们在测试过程中极力要避免地,否则,程序 员可能哪天心情很好,重构一下代码,破坏了一些网页的HTML结构但是从用户的角度来看又没有任何区别;这种代码重构,作为测试人员只能跟着程序员的代码重构,修改 测试代码,那个时候,你当然会希望改的地方越少越好啦。

对于这个 缺点,可能有人要说,在前面的文章“网站测试自动化系统—基于SeleniumVSTT”,创建博客的测试步骤不是已经被有效地封装成一个函数了吗,为什么还会说有冗余?这是因为在自动化 测试过程中, 测试人员会定期(一些高规格的软件开发团队要求每天)将所 以编写完毕的测试代码批量执行一遍,这就涉及到对于任何测试用例编码都非常重要的两个原则:

1) 一个测试用例可以独自执行成功,就是说如果是单独执行这一个测试用例的话,这个测试用例是可以执行成 功的否则就是产品编码的失误(Bug)。举个例子,你正要编码测试一个管理博客文章的功能,这个功能通常来说都是登录用户才可以使用的。 然而,也许你刚刚编码完毕一个登录方面的测试用例,而且用例执行完毕的时候,没有执行注销操作。这个时候你不能想当然地以为下一个测试用例一定就是你现在 正在编码的文章管理的测试用例。

因为测试 人员既保留有将多个测试用例任意排列执行的权力,也可以选择单独执行这一个测试用例比如程序员刚刚重构了文章管理功能的代码,为了节省测试时间,测试人员可能会选择只执行文章管理方面的测试用例。所以不要将自己的命运寄托在 别人手里。即除了整个团队都公认的前提以外,不要相信任何前提。

2) 测试用例可以在任意排列的用例序列中执行通过,因此测试代码应该尽量保护测试环境。举个例子,你设计 了一个管理用户权限的测试用例,一般来说这种功能只有管理员才有权限操作的。然而,也许另一个粗心大意的测试工程师编码了一个测试删除用户的用例,恰好将 管理员删除了,而你的用例正好在他的用例之后执行……己所不欲,勿施于人,既然你不希望碰到这种情况,那么在编码自己的测试用例之前也应该避免类似的事情 发生。

回过头 来再举评论管理测试用例的设计,于是你的几个测试代码可能看起来像下面这样:

[TestMethod]

public void BlogCommentIsDisabled()

{

TestLibrary.UserHelper.LogOnAsAdmin();

var blog = TestLibrary.BlogHelper.CreateBlog(博客文章标 题, 文章内容);

// 去管理文章的网页

TestLibrary.BlogHelper.ManageArticles();

// 在文章管理的网页的文章列表里依次 查找标题为

// “博客文章标题的文章连接,

var blogListItem = TestLibrary.BlogHelper.FindBlog(blog.Title);

// 并且在网页上点击浏览这个链接,打开阅读文章的网页

blogListItem.View();

// 评论这篇文章

TestLibrary.BlogHelper.Comment(blog);

// 然后执行一些验证判断评论功能的确 被禁用掉了

// …

}

[TestMethod]

public void DeleteBlogComment()

{

TestLibrary.UserHelper.LogOnAsAdmin();

var blog = TestLibrary.BlogHelper.CreateBlog(博客文章标 题, 文章内容);

// 去管理文章的网页

TestLibrary.BlogHelper.ManageArticles();

// 在文章管理的网页的文章列表里依次 查找标题为

// “博客文章标题的文章连接,

var blogListItem = TestLibrary.BlogHelper.FindBlog(blog.Title);

// 并且在网页上点击浏览这个链接,打开阅读文章的网页

blogListItem.View();

// 评论这篇文章

var comment = TestLibrary.BlogHelper.Comment(blog);

// 找到刚才的评论、删除评论,然后执 行验证确定

// 评论被删除掉

}

每个测试 用例单独执行的时候,都不会有任何问题,但是两个放在一起执行的时候,问题就来了,两个用例创建了同名的文章,这样就直接导致测试结果的不稳定。为了解决 这个问题,也许有人会创建一个随机生成文章标题的帮助类(Helper Class),这种编码的难度很大,因为需要确保文章的标题永远是唯一的(或许可以考虑Guid?)。

2. 节省测试的时间,在用例中执行过多的步骤也会增加测试时间。虽然测试团队都会在晚上批量执行自动化测 试用例,但是在产品开发的过程当中,测试用例通过率不能达到100%是很 正常的。对于每一个失败的测试用例,测试人员都要分析失败的原因判断是 产品的缺陷导致的,还是由于测试代码本身的问题引起的。额外的测试步骤也会相应地增加测试人员分析失败的时间(一般测试人员都会重新执行一遍测试代码来找 出问题原因)。

3. 增加不必要的测试用例失败,测试可以分好几块,一种是功能测试,也就是验证产品的功能是否可以正常工 作;一种是压力测试,即测试产品在极端情况下的执行情况;还有其他的例如性能测试,国际化测试等等。一般来说,不同的测试都会有自己的自动化测试用例集 合。如果在功能测试当中,用例代码在系统里面添加了很多冗余数据,执行的测试用例多了,必然导致网站的性能和反应速度会有所下降。而在测试代码中,一般都 会在执行一步操作以后,等待一段时间等网页的内容刷新。网站反应速度的 下降,直接导致测试失败。例如本来在编写测试代码的时候,3秒钟肯定会刷 新的网页,在测试执行的环境中,因为过多的冗余数据,30秒可能都打不开 一个网页。当然啦,网站反应速度的下降肯定是产品代码的缺陷,但是不应该将压力测试和功能测试混合起来做。

因此,我个人建 议,在测试过程中,例如前面举的评论功能的测试中,完全可以事先在网站的数据库中先创建好一篇或多篇专门用来做评论测试的文章。而每天晚上,在大规模执行 自动化测试用例之前,编写一个小的脚本,将网站的数据库替换成这个基准数据库。

又比如,为了测试 用户权限管理的功能,完全可以事先在网站的数据库当中先准备好一个管理员帐号,这个管理员帐号和密码可以当作一个常量,然后测试代码里都使用这个帐号来执 行权限管理的测试。例如下面的代码:

public class Consts

{

public const string TimeToWaitForPageToLoad = “30000”;

public const string AdminUserName = “administrator”;

public const string AdminPassword = “0123456”;

}

public class UserHelper : UIHelperBase

{

public UserHelper(TestLibrary settings)

: base(settings)

{

}

public void LogOnAsAdmin()

{

LogOn(TestLibrary.Consts.AdminUserName, TestLibrary.Consts.AdminPassword);

}

public void LogOn(string username, string password)

{

if (String.IsNullOrEmpty(username))

throw new CaseErrorException(new ArgumentNullException(“username”));

if (String.IsNullOrEmpty(password))

throw new CaseErrorException(new ArgumentNullException(“password”));

selenium.Open(“/”);

Thread.Sleep(2000);

if (selenium.IsElementPresent(“link=Log On”))

{

selenium.Click(“link=Log On”);

}

if (selenium.IsElementPresent(“link=Login”))

{

selenium.Click(“link=Login”);

}

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Type(“username”, username);

selenium.Type(“password”, password);

selenium.Click(“//input[@value=’Log On’]”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

}

}

在上面的代码中, 我也把等待网页刷新的时间设置成常量。对于在测试代码中使用事先在基准数据库中准备的测试数据,需要一点编程技巧。请先看下面的代码,下面的代码是一段记 录通过网页操作创建文章的代码:

public class Blog : UIHelperBase

{

// 博客的标题

public string Title { get; private set; }

// 博客的超链接

public string Permalink { get; private set; }

// 博客的超链接文本

public string MenuText { get; private set; }

public string Owner { get; private set; }

public Blog(TestLibrary settings, string title,

string permalink, string menutext, string owner)

: base(settings)

{

Title = title;

Permalink = permalink;

MenuText = menutext;

Owner = owner;

}

// 通过网页界面的操作创建一篇新文章

//

// PostSetting是一个结构,包含了一篇新文章的所有元素,

// 例如文章标题,内容等等.

public Post CreatePost(PostSettings settings)

{

if (settings == null)

throw new CaseErrorException(new ArgumentNullException(“settings”));

if (!String.IsNullOrEmpty(settings.Body))

throw new CaseErrorException(“Set post body is not implemented yet!”);

if (settings.PublishDateTime.HasValue)

throw new CaseErrorException(“PublishDateTime is not implemented yet!”);

// selenium这个变量,你可以想象成是一个正在浏览网页的 网友的封装

selenium.Open(“/”);

selenium.Click(“link=Admin”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Click(“link=Manage Blogs”);

selenium.WaitForPageToLoad(“60000”);

selenium.Click(String.Format(“link={0}”, Title));

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Click(“link=New Post”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

selenium.Type(“Routable_Title”, settings.Title);

selenium.Type(“Tags”, settings.Tags);

if (settings.Permalink != null)

selenium.Type(“Routable_Slug”, settings.Permalink);

if (settings.DisableNewComments)

selenium.Click(“CommentsActive”);

if (settings.PublishSetting == PostSettings.PublishSettings.PublishNow)

selenium.Click(“Command_PublishNow”);

else if ( settings.PublishSetting == PostSettings.PublishSettings.PublishLater )

throw new CaseErrorException(“PublishLater is not implemented yet!”);

selenium.Click(“submit.Save”);

selenium.WaitForPageToLoad(TestLibrary.Consts.TimeToWaitForPageToLoad);

return new Post(TestSettings, settings, this);

}

}

public class PostSettings

{

public enum PublishSettings

{

SaveDraft,

PublishNow,

PublishLater

}

public string Title { get; set; }

public string Permalink { get; set; }

public string Body { get; set; }

public string Tags { get; set; }

public bool DisableNewComments { get; set; }

public PublishSettings PublishSetting { get; set; }

public DateTime? PublishDateTime { get; set; }

}

public class Post : UIHelperBase

{

// 当初创建文章的原始详细信息

public PostSettings Settings { get; private set; }

// 文章的标题从网页上获取

public string Title { get { return selenium.Read(…); } }

// 下面省略文章相关的操作若干

// …

public Post(TestLibrary settings, PostSettings postSettings, Blog blog)

: base(settings)

{

Settings = postSettings;

ContainerBlog = blog;

}

// 下面省略文章相关的操作若干

// …

}

从上面的代码中, 你可以观察到,Post的属性,除了Settings属性以外,其他的属性都是从网页上直接读取的当然是假设当前网页正在显示对应的文章。因此,要将基准数据库集成到自动化测试代码中来,只要实例化 一个PostSettings变量就好了。TestLibrary 负责连接到Selenium-RC,并保存对应连接的类。下面的代码演示了这个思想:

public class TestLibrary

{

public UserHelper UserHelper { get; private set; }

public BlogHelper BlogHelper { get; private set; }

public CommentHelper CommentHelper { get; private set; }

public Blog DefaultBlog { get; private set; }

public Post DefaultPost { get; private set; }

public ISelenium Selenium { get; private set; }

public string SiteUrl { get; private set; }

public class Consts

{

public const string TimeToWaitForPageToLoad = “30000”;

public const string AdminUserName = “administrator”;

public const string AdminPassword = “0123456”;

}

public TestLibrary(ISelenium selenium)

{

this.UserHelper = new UserHelper(this);

this.BlogHelper = new BlogHelper(this);

this.CommentHelper = new CommentHelper(this);

Selenium = selenium;

InitialDefaultSiteDate();

}

private void InitialDefaultSiteDate()

{

DefaultBlog = new Blog(this, “Default Test Blog”, “default-test-blog”, “Default Test Blog”, Consts.AdminUserName);

DefaultPost = new Post(this, new PostSettings()

{

Title = “Default Test Post”,

Permalink = “default-test-post”,

Body = “This is for web site testing purpose.”,

Tags = “Test”,

PublishSetting = PostSettings.PublishSettings.PublishNow

},

DefaultBlog);

}

}

下面是TestLibrary的完整源代码:

public class TestLibrary

{

public UserHelper UserHelper { get; private set; }

public BlogHelper BlogHelper { get; private set; }

public CommentHelper CommentHelper { get; private set; }

public Blog DefaultBlog { get; private set; }

public Post DefaultPost { get; private set; }

public ISelenium Selenium { get; private set; }

public string SiteUrl { get; private set; }

public class Consts

{

public const string TimeToWaitForPageToLoad = “30000”;

public const string AdminUserName = “administrator”;

public const string ContributorUser = “Contributor1”;

public const string AuthorUser = “Author1”;

public const string ModeratorUser = “Moderator1”;

public const string EditorUser = “Editor1”;

public const string CommonPassword = “0123456”;

public const string AdminPassword = “0123456”;

public const string DefaultSeleniumHost = “localhost”;

public const int DefaultSeleniumPort = 4444;

public const string DefaultBrowser = “*firefox”;

public const string DefaultSite = “http://localhost:30320”;

}

public TestLibrary(ISelenium selenium)

{

this.UserHelper = new UserHelper(this);

this.BlogHelper = new BlogHelper(this);

this.CommentHelper = new CommentHelper(this);

Selenium = selenium;

InitialDefaultSiteDate();

}

private void InitialDefaultSiteDate()

{

DefaultBlog = new Blog(this, “Default Test Blog”, “default-test-blog”, “Default Test Blog”, Consts.AdminUserName);

DefaultPost = new Post(this, new PostSettings()

{

Title = “Default Test Post”,

Permalink = “default-test-post”,

Body = “This is for web site testing purpose.”,

Tags = “Test”,

PublishSetting = PostSettings.PublishSettings.PublishNow

},

DefaultBlog);

}

public static TestLibrary SetupTest(TestContext testContext)

{

if (testContext != null && testContext.DataRow != null && testContext.DataRow.Table.Columns.Contains(“seleniumHost”))

{

return SetupTest(testContext.DataRow[“seleniumHost”].ToString(),

Int32.Parse(testContext.DataRow[“seleniumPort”].ToString()),

testContext.DataRow[“browser”].ToString(),

testContext.DataRow[“site”].ToString());

}

else

{

return SetupTest(Consts.DefaultSeleniumHost, Consts.DefaultSeleniumPort,

Consts.DefaultBrowser, Consts. DefaultSite);

}

}

public static TestLibrary SetupTest(string seleniumHost, int seleniumPort,

string browser, string site)

{

var selenium = new DefaultSelenium(

seleniumHost, seleniumPort, browser, site);

selenium.Start();

return new TestLibrary(selenium) { SiteUrl = site };

}

public void Shutdown()

{

try

{

Selenium.Stop();

}

catch (Exception)

{

// Ignore errors if unable to close the browser

}

}

}

未完待续……

[转载]淘宝API开发系列--开篇概述

mikel阅读(1251)

[转载]淘宝API开发系列–开篇概述 – wuhuacong(伍华聪)的专栏 – 博客园.

前阵子有空发现淘宝的API挺有意思的,就留意学习了一下,淘宝从2008.6月开始公测以来,API渐趋稳定,文档的相关说明也比较细致,不过基 于淘宝API做应用的软件目前应该还不是很多,不过由于马云的影响力,淘宝API终会发光发热,给我们做开发的程序员多一份选择,多一个摆弄的东西。

在学习中,发现有一些比较好玩,比较有趣的地方,如果有空继续的话,准备开一个系列,总结介绍一下相关的知识,希望能够给博客多一份人气,多一份积 累。

淘宝开放平台(Taobao Open Plateform,又叫TOP),地址是http://open.taobao.com/ , 我们看看其官方对TOP的描述:

淘宝开放平台(即TOP,无特别说明下文中TOP即指淘宝开放平台)对各类合作伙伴所提供的产品可以概括为两个平台,三条支撑主线。两个平台指的是Open.taobao.comApp.taobao.com ;三条支撑主线是开放数据和业务流程、开放淘宝自有插件平台、开放对外接入标准,关于这两个产品和三条业务支撑线。

和众多平台一样,如Google,淘宝一样需要开发者注册一个账号,淘宝每个应用需要注册一个应用程序键(App Key),淘宝就是基于应用程序键来控制用户的访问频率和流量的,另外可以通过应用程序键,让使用者登陆确认,获取到相关的授权码,然后获取 SessionKey,作为访问使用者淘宝资源(如买入卖出等私人记录的信息)。


淘宝的授权码是在用户确认后产生,并且只能使用一次,用来生成SessionKey,一般来说,SessionKey间隔的调用API事件不能超过 10分钟,否则就会失效,需要重新获取用户的授权码,然后再次生成SessionKey。这样的做法虽然比较麻烦,但是对保证使用者的淘宝资源确是比较不 错的做法。大概的界面如下所示:

淘宝的论坛有各种语言开发的介绍,相对来说还是比较详细,开发者有什么问题,在这里都可以问问,论坛地址是:http://open.taobao.com/bbs/forum.php

淘宝的API是以REST服务提供了,通过HTTP访问,返回相关的信息,格式可以是Json格式的,也可以是XML格式的,各取所需吧。

有一位能人把淘宝的API做了进一步的封装,就是TOP4NET的类库,封装了各种API资源的访问,并且把信息封装为实体类对象进行传递,使用起 来还是不错的,本篇后面会进一步介绍一下基于TOP4NET的基础上做淘宝的应用开发。

TOP4NET 是作为开源组件提供的,其SVN地址是http://code.google.com/p/top4net/source/browse/#svn/trunk/Top4Net, 大家可以下载下来研究研究,做一定的修改补充,代码风格写得不错,赞一个。

主要研究技术:代码生成工具、Visio二次开发

转载请注明出处:
撰写人:伍华聪  http:
//www.iqidi.com

[转载]web报表开发技术专题六:尴尬的Web打

mikel阅读(1077)

[转载]web报表开发技术专题六:尴尬的Web打印 – webreport – 博客园.

自从电脑被发明以来,将电脑中的内容打印出来是自然而然的需求。进入互联网时代后便更是如此。但要想完美地实现Web打 印功能却并非易事,甚至于根本都不可能彻底地解决好这个问题。

记得很早以前田荣举(注:田荣举现任金蝶集团高级副总裁兼任首席技术官)和我私下聊 过:“打印功能要占到整个系统开发量的两成”。打印功能是一个系统开发的重要组成部分。中国人历来好面子,往往要求有漂亮的打印,以显露科技之高深。打印 功能对于web报表工具来说就更是不可或缺的了。

因为打印功能的重要性,所以进入web开发后,很快就开始关注web打 印功能的开发了,web打印有下面的相关问题需要考虑清楚:

1 在客户端还是服务器端打印?考虑到数据传递到客户端的速度问题,在服务器 端做大数据量的打印还是不错的选择。但在后台打印必竟很不方便,所以主要还是要在客户端打印,即利用客户端的打印机打印。

2 是导出为PDF或EXCEL文件后打印还是直接打印?将要打印的内容生成 为PDF或EXCEL文件后再打印,这也是一种方式,这种方式实际上也就几乎不需要专门考虑打印功能的开发了,开发工作量少。但其缺点也很明显:需要客户 端安装打开PDF的软件(或EXCEL软件),需要在另一个软件中打印,步骤多,无法实现点击后直接打印,这种打印模式也无法实现某个单据是否打印了要当 作一个标志保存起来的场合。

3 是利用IE的打印还是自己实现打印?如果打印功能全部由自己实现,因为要 处理数据,样式,位置,图片等的打印,自然程序量小不了,往往会使第一次的下载量很大。而且往打印控件中传递要打印的数据的过程会比较复杂而且慢。

4 第一次打印时需不需要下载ActiveX控件?如果不需要,则无法用 JavaScript脚本程序控制客户端的打印机,无法自定义纸张,无法不弹出打印对话框就直接打印。也就是说,最终用户在每次打印时都要手工选择好打印 参数(如打印方向,纸张大小等),这样显然会使最终用户的操作比较麻烦。

最开始我是设想直接用JavaScript脚本来计算分页,直接利 用IE中的打印机制来解决打印问题。后来随着IE的 版本升级,安全机制不断完善。在不下载ActiveX插件的情况下,IE的 安全机制是不可能让你用JavaScript脚本来全面控制客户端的打印机的。试想如果这样的话就 有可能浏览网上的某个网页时,你连接的打印机就自动开始打印东西了。也就是说,要想弄好Web打印,必须要在客户端下载插件。 在中国流氓插件泛滥的情况下,大家一看到插件都怕了。要不就要下载插件,要不打印就不太方便。这成了web打 印的死结。且觉得一样子无法解开,真是尴尬的Web打印

既然最佳的方式是无解的,哪就退而求其次吧。用一个小的轻量级的activeX控 件来控制客户端的打印机吧,这样第一次下载这个activeX控件是也不会很慢。

记得以前在卖web打印控件时,有一用户将scriptx网站告诉我,说要能实现这样的功能就买。于是便开始研究起scriptx来。按照我以前用visual c++编程的经验,觉得大体就是用钩子函数来实现。结合IE的打印模 版技术,终于基本上达到了。用visual c++ATL(为 了减少字节而没有用MFC)做了一个webprint.dll, 这个webprint.dll压缩成cab文 件后只有75K,和一个网页的大小相当。这样在第一次下载时便不会太慢了。因为真正的打印机制还是 调用IE内部的,并没有在webprint.dll里 面实现具体的打印功能,所以就不需要将要打印的内容传递到webprint.dll中,这是区别于 其它用activex控件实现web打印的 重要一点。如果靠activex控件来实现全部的打印功能的话,就必然要求要将打印的内容传递到activex控 件中,这个传递的过程会比较麻烦而且速度也慢。Webprint.dll只完成向IE的 内部实现的打印功能模块传递打印的基本参数(如:页面大小,页边距,打印方向,直接打印),网页上显示什么内容就打印什么内容,没有向webprint.dll传 递打印内容的过程。如下图所示的打印预览界面图:

实际上,打印的需求很简单,无非就是:分页,页眉页脚,页边距,打印方向,自定义纸张,直接打印,弹出打印对话框再打印, 预览,放缩打印,成批打印,套打等功能,打印的需求很固定,明确而且通用。将一个多页的报表一页页地打印出来这样一个简单的需求,在网页上却相当麻烦,我 也是在不久前才将它在e表中算是比较理想的实现了,即可以象cs程 序一样,点一下按钮,就将多页的报表一页页地打印出来。这其中要考虑到在互联网上从服务器端向客户端传递大量数据时的分页处理,以及打印了一页数据再传递 另一页数据的问题。因为如将要打印的多页内容一次从服务器端全部发送到客户端的话,在当今互联网的条件下很可能会因速度慢而受不了。只能每次从服务器端发 送一页数据到客户端,打印,然后再发送下一页数据到客户端,再打印,如此下去直至打印结束。这一点也是web打 印比c/s程序的打印要麻烦的地方。

总之,Web打印问题已经和浏览网页速度慢的问题一样,成为Web程 序的难题之一。只能作一些权衡。比如象国外因为PDF查看工具的流行,大多选择生成PDF文 件后再打印的方式。我研究Web打印解决方案多年,最终采用的轻量级的ActiveX控 件的Web打印方案,是我现在认为最适合中国国情的方案。也许以后还能找到更好的。

待 续…

相关链接:
[原创]web报表开发技术专题一:序号问题
[原创]web报表开发技术专题二:报表工具的核心—数据集的变换
web报表开发技术专题三:不规则分组和跨行组运算
web报表开发技术专题四:开发报表设计器的背后故事
web报表开发技术专题五:Excel的阴影
web报表工具的制表效率分析
几种web报表打印方案的比较

[转载]归纳asp.net mvc学习中应该要掌握的内容

mikel阅读(858)

[转载]归纳asp.net mvc学习中应该要掌握的内容 – longgel – 博客园.

自从自己学习ASP.NET mvc 有过一段时间了,今天回想一下之前学习ASP.NET mvc模型中的几个把准方向的内容,今天在这里归纳一下,也许会ASP.NET MVC不了解的园友有所帮助。

当然首先要弄清楚第一个的 就是MVC这个的概念,Controller,View,Model三者之间的关系。

我刚开博的时候,就有写关于asp.net MVC学习的一些相关文章,说明一下,其实我开博的原因第一就是有一个技术交流的平台,第二就是提升自己的写作能力以及表达能力。下面也列出之前写过的相 关MVC概念上的内容。写的不好,请多多指出,谢谢。

(asp.net MVC学习)对ASP.NET MVC框架的了解

(asp.net MVC学习)控制器Controller以及Action

(asp.net mvc学习) 视图View的使用以及与Controller之间的数据传递

其 次学习ASP.NET MVC框架中有一个必须了解的内容就是URL Routing,虽然它并不是asp.net mvc框架中的内容,但是asp.net mvc能够使用都必须靠它。我也有写过关于URL Routing的文章,虽然都写的不好,但是对于需要入门的朋友应该是有帮助的。^_^

(ASP.NET MVC学习)url Routing 使用与介绍

另外还有就是ActionFilter,asp.net mvc中的一些帮助类,如HtmlHelper,UrlHelper的使用,这些之前也有写过,只不过我今天看了之前写的,感觉水平太差,今天还是推荐给 大家,希望大家不要见怪(不爽的都可以骂我的),因为之前都是没有什么代码的,这样讲大家可能是不能很好的理解,今后会贴上相关的代码。

(asp.net mvc学习)ASP.NET MVC ActionFilter的学习与使用

(asp.net MVC学习)System.Web.Mvc.HtmlHelper学习及使用

(asp.net MVC学习)System.Web.Mvc.UrlHelper的学习与使用

还 有在这里本人不得不提的就是Controller和view之间使用强类型数据,我也觉得很重要,之前就没有写这方面的文章,后面我将会把这一节给补上。

因为我的水平很低,而且写作和表达能力都比较差,所以写出来的内容只能给准备学asp.net mvc的朋友一些指引,对于大牛者,希望你们帮我指出问题,并且如果大牛们有相关的文章,都可以贴给我看一下,我也好跟着大牛们学习学习。谢谢

[转载]全国省市区县最全最新数据表(数据来源谷歌) - Capricornus - 博客园

mikel阅读(790)

[转载]全国省市区县最全最新数据表(数据来源谷歌) – Capricornus – 博客园.

因为工作项目需求,需要一个城市县区数据表,上网搜了下,基本都不全,所以花了3天时间整理了一遍.

省市区县数据来源Google地图. (包括34个省 , 371个市, 2824个县区)

1 /**********创建省级表**********/
2 CREATE TABLE T_Province
3 (
4 ProID INT IDENTITY(1,1) PRIMARY KEY, --省份主键
5 ProName NVARCHAR(50) NOT NULL, --省份名称
6 ProSort INT, --省份排序
7 ProRemark NVARCHAR(50) --说明
8 )
9 Go
1 /********创建市级表********/
2 CREATE TABLE T_City
3 (
4 CityID INT IDENTITY(1,1) Primary KEY , --城市主键
5 CityName NVARCHAR(50) NOT NULL, --城市名称
6 ProID INT, --所属省份
7 CitySort INT --城市排序
8 )
1 /*********城市县区表*********/
2 CREATE TABLE T_District
3 (
4 Id INT IDENTITY(1,1) PRIMARY KEY,
5 DisName NVARCHAR(30) NOT NULL, --区县名称
6 CityID INT NOT NULL, --所属城市
7 DisSort INT --区县排序
8 )


数据下载地址: http://files.cnblogs.com/Capricornus/AllCityData.rar

[转载]C#自动实现Dll(OCX)控件注册的两种方法 - 幸运星空 - 博客园

mikel阅读(1024)

[转载]C#自动实现Dll(OCX)控件注册的两种方法 – 幸运星空 – 博客园.

尽管MS为我们提供了丰富的.net framework库,我们的程序C#开发带来了极大的便利,但是有时候,一些特定功能的控件库还是需要由第三方提供或是自己编写。当需要用到Dll引用 的时候,我们通常会通过“添加引用”的方式将它们纳入到项目中,然后就可以像使用自己的类一样方便的使用它们了。但是,有些Dll库(OCX)文件是需要 注册到Windows注册表后才能正常添加和使用的。本文介绍两种为Dll库(OCX)自动注册的方法,为大家提供参考。

首先,大家都知 道在Windows的“运行”中,输入“Regsvr32.exe 路径”这样的方法来手动注册Dll控件(OCX),显示这种方法对于程序的自动化部署等带来极大的不便,因此,今天我们着重介绍如何用C#实现自动注册。

方法一:调用Regsvr32法

既然可以在运行栏中输入“Regsvr32.exe 路径”的方法来注册,那么,一定可以在C#程序中采用同样的方法来调用Regsvr32,以实现注册:

Process p = new Process();
p.StartInfo.FileName = "Regsvr32.exe";
p.StartInfo.Arguments = "C:\\DllTest.dll";//路径中不能有空格
p.Start();

采用这种方法,注意要添加对命名空间System.Diagnostics的引用:

using System.Diagnostics;

另 外,这种方法有一个不足之处,那就是注册工作是在本程序之外由Regsvr32.exe程序来完成的,系统内不方便知道注册的结果,也不方便对注册过程弹 出的对话框进行自定义和控制,而且每次注册完,不管成功与否,都会有一个提示框出现,这对于只想在程序载入时悄悄完成注册工作的朋友来说,简直是不可接受 的。总之,这种方法简单,但是自主性,不大实用。

方法二:调用DllRegisterServer函数法

既然方法一不大实用,那么我们就来寻找一种真正实用的方法来达到我们的目的吧。研究Regsvr32.exe和Dll文件,我们会发现,其实每个需 要注册的文件都包括一个DllRegisterServer()方法,Regsvr32.exe就是通过调用该方法来完成Dll的注册的。呵呵,知道了这 个,我们就可以自己调用DllRegisterServer()来完成注册过程啦。

首先,还得引入外部方法:

[DllImport("DllTest.dll")]
public static extern int DllRegisterServer();//注册时用
[DllImport("DllTest.dll")]
public static extern int DllUnregisterServer();//取消注册时用
接下来就不难啦:
int i = DllRegisterServer();
if (i >= 0)
{
    //注册成功!
}
else
{
    //注册失败
}

取 消注册的过程就不应再贴代码啦。

两种方法介绍完啦,可是好像还缺点什么?对了,那就是对Dll是否已经注册过了的判断。一般情况下,我们可以将对Dll控件的注册过程放在系统启动 的过程中来完成,但是,总不能每次启动都注册一次吧?这样做显然不合理。那么,我们就来判断一下,当前Dll是否已经注册过,如果已注册过,就跳过注册过 程。

每一个Dll的注册都会在注册表里记录下有关它本身的资料,如注册路径,唯一ID等。我们这里就是利用它留下的唯一ID号来判断:

RegistryKey rkTest = Registry.ClassesRoot.OpenSubKey("CLSID\\{7713F78A-44DE-42BA-A1F6-3FB0BD6CA63B}\\");
if (rkTest == null)
{
    //Dll没有注册,在这里调用DllRegisterServer()吧
}

注意要添加对命名空间Microsoft.Win32的引用:

using Microsoft.Win32;

其中的“{7713F78A-44DE-42BA-A1F6-3FB0BD6CA63B}”就是该Dll的唯一ID啦,每一个Dll文件都会不一样 的。但是,问题又来了,怎么样知道它的唯一ID呢?其实很简单,那就是“逆向思维”。我们可先注册这个Dll文件,然后到注册表的 “HKEY_CLASSES_ROOT\CLSID”分支下“查找”Dll的名称或路径,就可以看到这个ID啦。简单我就不多说啦。

写到这里,该说的问题总算说完啦。大家如果还有什么疑问的话可以回帖提出。

谢谢!
《完》

[转载]SQLServer查询成本:SET SHOWPLAN_ALL

mikel阅读(1528)

[转载]SET SHOWPLAN_ALL.

语法

SET SHOWPLAN_ALL { ON | OFF }

注释

SET SHOWPLAN_ALL 的设置是在执行或运行时设置,而不是在分析时设置。

当 SET SHOWPLAN_ALL 为 ON 时,SQL Server 返回每个语句的执行信息但不执行语句,而且 Transact-SQL 语句将不执行。将该选项设置为 ON 后,将返回有关所有后续 Transact-SQL 语句的信息,直到将该选项设置为 OFF 为止。例如,如果在 SET SHOWPLAN_ALL 为 ON 时执行 CREATE TABLE 语句,则 SQL Server 从涉及同一个表的后续 SELECT 语句返回错误信息;指定的表不存在。因此,对该表的后续引用将失败。当 SET SHOWPLAN_ALL 为 OFF 时,SQL Server 执行语句但不生成报表。

SET SHOWPLAN_ALL 是供为处理其输出而编写的应用程序使用的。使用 SET SHOWPLAN_TEXT 返回可由 Microsoft MS-DOS® 应用程序(如 osql 实用工具)读取的输出。

在存储过程内不能指定 SET SHOWPLAN_TEXT 和 SET SHOWPLAN_ALL,它们必须是批处理中的语句。

SET SHOWPLAN_ALL 将信息作为行集返回,行集形成一个层次结构树,用以表示 SQL Server 查询处理器在执行每个语句时所采取的步骤。输出中反映的每个语句都包含一个含有语句文本的行,后面跟有几个含有执行步骤详细信息的行。下表显示输出中包含 的列。

列名 描述
StmtText 对于不是 PLAN_ROW 类型的行,该列包含 Transact-SQL 语句的文本。对于 PLAN_ROW 类型的行,该列包含对操作的描述。该列包含物理运算符,也可以选择包含逻辑运算符。该列还可以跟有由物理运算符决定的描述。有关更多信息,请参见逻辑运算符和物理运算符
StmtId 当前批处理中的语句数。
NodeId 当前查询内的节点 ID。
Parent 上一级步骤的节点 ID。
PhysicalOp 节点的物理实现算法。仅限于 PLAN_ROWS 类型的行。
LogicalOp 该节点表示的关系代数运算符。仅限于 PLAN_ROWS 类型的行。
Argument 提供有关所执行操作的辅助信息。该列的内容取决于物理运算符。
DefinedValues 包含该运算符所引入值的用逗号分隔的列表。这些值可以是出现在当前查询(例如,在 SELECT 列表或 WHERE 子句中)内的计算表达式,或者是由查询处理器为处理该查询而引入的内部值。以后在该查询内的任何其它地方都可以引用这些定义的值。仅限于 PLAN_ROWS 类型的行。
EstimateRows 由该运算符输出的预计行数。仅限于 PLAN_ROWS 类型的行。
EstimateIO 该运算符的预计 I/O 成本。仅限于 PLAN_ROWS 类型的行。
EstimateCPU 该运算符的预计 CPU 成本。仅限于 PLAN_ROWS 类型的行。
AvgRowSize 正通过该运算符传递的行的预计平均行大小(以字节为单位)。
TotalSubtreeCost 该操作和所有下一级操作的预计(累积)成本。
OutputList 包含当前操作所计划的列的用逗号分隔的列表。
Warnings 包含与当前操作相关的警告信息的用逗号分隔的列表。警告信息可以在列的列表中包含字符串”NO STATS:()”。该警告信息表示查询优化器曾尝试根据该列的统计做决策,但没有可用的统计。因此,查询优化器不得不进行推测,这可能已导致选择低效的 查询计划。有关创建或更新列统计(这些统计有助于查询优化器选择更高效的查询计划)的更多信息,请参见 UPDATE STATISTICS。该列可以选择包含字符串”MISSING JOIN PREDICATE”,这表示正在进行的联接(与表有关)未使用联接谓词。意外地除去联接谓词可能导致查询的时间比预期长得多,并返回巨大的结果集。如果 出现该警告,请验证是否有意除去了联接谓词。
Type 节点类型。对于每个查询的父节点,这是 Transact-SQL 语句类型(如 SELECT、INSERT、EXECUTE 等)。对于表示执行计划的子节点,这是 PLAN_ROW 类型。
Parallel 0 = 运算符没有以并行方式运行。
1 = 运算符正在以并行方式运行。
EstimateExecutions 该运算符预计在当前查询运行期间将执行的次数。
权限

SET SHOWPLAN_ALL 权限默认授予所有用户。

示例

下面两个语句使用 SET SHOWPLAN_ALL 设置,显示 SQL Server 在查询内对索引的使用进行分析和优化的方法。

第一个查询在索引列上的 WHERE 子句中使用等于比较运算符 (=)。这在 LogicalOp 列内产生聚集索引查找值,在 Argument 列内产生索引名。

第二个查询在 WHERE 子句中使用 LIKE 运算符。这将强制 SQL Server 使用聚集索引扫描并查找满足 WHERE 子句条件的数据。这在含有 Argument 列内索引名的 LogicalOp 列内产生聚集索引扫描值,在含有 Argument 列内 WHERE 子句条件的 LogicalOp 列内产生筛选值。

EstimateRowsTotalSubtreeCost 列内用于第一个基于索引的查询的值较小,这表示与非索引查询相比,该查询的处理速度快得多且使用更少的资源。

USE pubs
GO
SET SHOWPLAN_ALL ON
GO
-- First query.
SELECT au_id 
FROM authors 
WHERE au_id = '409-56-7008'
GO
-- Second query.
SELECT city
FROM authors
WHERE city LIKE 'San%'
GO
SET SHOWPLAN_ALL OFF
GO

请参见

SET

SET SHOWPLAN_TEXT