Switching Versions of the ASP.Net Framework – The Fast Way

By Scott Forsyth
November 28, 2003

When ASP.NET v1.1 was released, it became my project to find the best way to support this for our client base at ORCS Web, Inc. We needed to be able to switch versions of the Framework per site as smoothly as possible. Requiring everyone to move to version 1.1 wouldn’t work and leaving everyone on version 1.0 wouldn’t work either, so we had to have a way to pick and choose and to make the switch without interrupting other sites on the same server.

Is this for you?

Before I go any further, I should mention the reasons why this applies to the average developer as well as the system administrator. Currently there are two released versions of ASP.NET but there are already multiple alpha and beta versions and there will continue to be more. To be able to quickly switch between versions with just a couple clicks and keystrokes makes development and testing against multiple frameworks easy. Granted, aspnet_regiis.exe will accomplish this as I’ll mention below, but it can take over 30 seconds. Imagine being able to immediately switch between Version 1.0, 1.1 and 2.0 (v2.0 is in Alpha at the time of this writing) without setting up multiple sites in IIS. If this interests you, read on because this doesn’t just apply to a system administrator.

Objectives

My 4 objectives were:
1) To be able to update a single site to the new framework
2) To be able to revert back if desired
3) To have an easy way for anyone on our team to be able to do it     consistently and quickly
4) To do this without affecting the other sites on the same server

Stating the Obvious

The most obvious way to do this was to use aspnet_regiis.exe supplied with ASP.NET. To use this method I would run “C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_regiis.exe -s W3SVC/50/ROOT”. This would update the Version of the framework for the site with ID 50 and not update the other sites on this server. It has the flexibility required to set this for the whole server, per site or even per sub-folder. The problem I immediately ran into was that both Internet Information Server (IIS) service and Session State would reset, causing every site on the server to be down for up to 2 minutes. Ouch. The strange thing is that the whole server was affected, even when running this against a single site. This met the first 3 objectives. Unfortunately all sites on that server would be affected, blowing the 4th objective out of the water.

The other option I considered is manually changing all the script mappings in the IIS snap-in. To change this manually, select the “Home Directory” tab in IIS and click on the “Configuration” button. The “Mappings” tab has all the mappings. The path to the alternate aspnet_isapi.dll can be changed here. The problem is that this didn’t meet the 3rd objective because of the large number of script mappings that had to be manually changed.

Is a better solution possible?

Needless to say, I had to go back to the drawing board to find something that would work better. My initial guess was that doing this from script would be difficult and would probably cause a similar interruption of service to IIS. Fortunately I was wrong on both points. Not only did it turn out to be a simple undertaking but there was zero, yes zero, impact on other sites. Even the site being changed was not affected, other than Session State being reset. That not only met all four of my objectives, it exceeded my expectations.

The Solution

Using WSH I use an Input box and ask for the server, Site ID (more on the later) and the version to change to. Then ADSI is used to retrieve the current script mappings and do a search-and-replace from the previous version to the new version.

There are a number of ways to obtain the siteID in IIS. My favorite is to go into the properties of the site from the IIS snap-in and click on the “Properties” button in the Logging section. At the bottom, you’ll notice a “Log file name”. It will start with W3SVC??. For example, it might be W3SVC1 or W3SVC99. It’s the number after the W3SVC that counts. (i.e. W3SVC1 is SiteID 1, W3SVC99 is SiteID 99).

At the risk of boring the casual reader I feel I must mention one more thing that was tested. The script mappings are usually inherited from the default mappings for the server. IIS uses its own storage system called the Metabase which is similar in concept to Windows Registry. The first time a site or sub-folder within a site is assigned specific script mappings, it will write its own copy of the script mappings to its area of the metabase. Any new changes to the script mappings will be written against the new copy. ChangeFramework.vbs will work correctly whether or not the custom script mappings are in place.

To use this script, simply save to your computer and double-click. This is freely available to use by all, but I request that you keep the header in place to give credit to both myself and ORCS Web, Inc. There is no warranty whatsoever so if your computer blows up or falls over dead, don’t come looking to me for help. But, the disclaimer aside, we’ve been running this for over 1/2 year on live production servers and it was worked 100% of the time without incident and I personally attest to its stability and trustworthiness.

Download

The script itself can be found at http://www.orcsweb.com/articles/ChangeFramework.zip

It should be pretty obvious by this doesn’t install the framework. It simply switches between versions that are already installed on your computer.

Scott Forsyth is Director of IT at ORCS Web, Inc. – a company that provides managed hosting solutions for clients who develop and deploy their applications on Microsoft Windows platforms. Services include shared hosting, dedicated hosting, and webfarm hosting, with specialty in .Net, SQL Server, and architecting highly scalable solutions.

Quote: http://www.orcsweb.com/articles/change_framework.aspx

ASPNET_regiis.exe Tool, Setting the Default Version Without Forcing an Upgrade on All Sites

Starting in the first version of ASP.NET, Microsoft has provided a tool to control which version of the framework is registered in IIS. This tool, aspnet_regiis.exe, is quite flexible and with the right understanding of how IIS and ASP.NET work, can be used for most any situation.

The most common parameter is -i. This is the full blown registration of the framework in IIS, including all sites and all vdirs. For example, let’s say you wanted your entire server to be registered using ASP.NET v2.0, then you would navigate to the v2.0 framework folder (%windir%\Microsoft.NET\Framework\v2.0.50727) and type aspnet_regiis -i. Likewise, you can navigate to (%windir%\Microsoft.NET\Framework\v1.1.4322) and run the same command to set v1.1 on all sites. This is a great way to repair a broken installation or force a particular framework version to apply to your whole server.

But this catch-all registration is too aggressive for many production servers. That’s where the other parameters come in.

To see a detailed list of commands, type aspnet_regiis /? from the command prompt. I’m going to explain two of them: -lk and -sn. With these commands, we can apply the script mappings on just some of the sites on the server and update the default.

Careful on Production Servers

Note: First a disclaimer. Running any aspnet_regiis commands that make a change to IIS will cause all sites on the server to have a short interruption of service. Even if you run the path specific commands which I’ll explain below, all sites will have some downtime. To make changes on production servers without any downtime, you’ll need to write a solution to update the script mapping directly. Here’s an old article of mine that explains this in more depth:
http://www.orcsweb.com/articles/change_framework.aspx (Someday I’ll rewrite that article with an updated script to handle more than 2 versions of the framework, and IIS6 application pool support.)

While I’m offering disclaimers, make sure not to run multiple versions of the framework in the same Application Pool in IIS6.0:

http://weblogs.asp.net/owscott/archive/2006/01/26/436607.aspx

IIS Inheritance

Let me digress again and explain how IIS inheritance works for the script mappings. IIS has a path structure similar to the file system on your computer. For example, the web part of the structure starts with /W3SVC/. Site #1 stores the script mappings in /W3SVC/1/ROOT/. Site #51 stores them in /W3SVC/51/ROOT/, and so on. If the script mappings are specifically set on a particular path then IIS will use those settings. But if they aren’t set then it will use the settings from the site or server root.

ASP.NET Version Defaults

When ASP.NET v1.1 was released, if you ran the install, it would apply your settings to all sites on the server, thus changing your framework version from v1.0 to v1.1. This meant that you had to be careful if you wanted to keep version 1.0 as the default. The install had some command line options to give more flexibility.

Version 2.0 of the framework is different and not as aggressive during the installation. Now it doesn’t make itself the default and you are required to specifically set it as the default if that is what you want. This is a good thing and is a better behavior than the v1.1 installation.

Don’t forget to take a backup

Warning: At this point, make sure to get a backup of IIS before continuing. If you do something wrong, you can restore it and everything will be brought back to its original state.

Displaying The Current Settings

Displaying The Current Settings

The aspnet_regiis tool has a neat way to see what is currently applied in IIS right now. You can do this by typing aspnet_regiis -lk from any framework folder. This will give something like this: (you can do this read-only command without any downtime on your server)

W3SVC/ 1.1.4322.2300
W3SVC/1/ROOT/ 2.0.50727.0
W3SVC/3/root/ 1.1.4322.2300
W3SVC/6/root/ 1.1.4322.2300
W3SVC/7/root/ 2.0.50727.0

Notice on this test server that the root, site #3 and site #6 are using v1.1 of the framework and site #1 and #7 are using v2.0. (Here is an article to find out which site is using which SiteID: http://weblogs.asp.net/owscott/archive/2005/07/29/421058.aspx)

Notice the gaps in numbers as #2, #4 and #5 aren’t set. This means that they will inherit their settings from /W3SVC/ which is currently using v1.1.

Now, the issue we have is that when setting up new sites, they will always use version 1.1 of the framework. Since v2.0 has all of the latest and greatest features, we want new sites to use v2.0 but make sure that existing sites aren’t changed. In our example here, let’s say that we want to upgrade the server to use v2.0 of the framework as the default version, but we don’t want sites #2, #3 or #6 to be upgraded.

If we run aspnet_regiis -i then we’ll force everything to version 2.0 which we don’t want. And if we upgrade just the root, then too many of the sites will be upgraded, which is also what we don’t want.

The Magic

The trick is to force all sites that you don’t want to upgrade to use v1.1, and then we can upgrade the root to v2.0. Then all new sites created after this will use v2.0.

Sites #3 and #6 are easy, they are already done for us. But site #2 needs to be set to specifically use v1.1 of the framework. This can be done by running aspnet_regiis from the v2.0 framework folder using this command: aspnet_regiis -sn W3SVC/2/ROOT/. Now running aspnet_regiis -lk will return this:

W3SVC/ 1.1.4322.2300
W3SVC/1/ROOT/ 2.0.50727.0
W3SVC/2/Root/ 1.1.4322.2300
W3SVC/3/root/ 1.1.4322.2300
W3SVC/6/root/ 1.1.4322.2300
W3SVC/7/root/ 2.0.50727.0

For the purpose of this example, we’re going to let site #4 and #5 continue to inherit from the root, so after the next step they will be upgraded (using inheritance) to use version 2.0 of the framework.

The final step is to update the W3SVC/ node without touching anything else. As I’m sure you’ve guessed by now, this is done using: aspnet_regiis -sn W3SVC/.  Now we’re done, let’s look at the final result:

W3SVC/ 2.0.50727.0
W3SVC/1/ROOT/ 2.0.50727.0
W3SVC/2/Root/ 1.1.4322.2300
W3SVC/3/root/ 1.1.4322.2300
W3SVC/6/root/ 1.1.4322.2300
W3SVC/7/root/ 2.0.50727.0

Success! Now sites #4 and #5 and all new sites will use the W3SVC/ setting, which is v2.0. Sites #1 and #7 will also use v2.0 because they were specifically set. Sites #2, #3 and #6 will continue running version 1.1 of the framework.

Summary

We’ve applied version 2.0 of the framework as the system default while allowing some sites to continue to run version 1.1 of the framework.

[转]IIS ASP.NET 1.1与2.0共存

各位在同一电脑上同时安装并运行.net 1.1 和 .net 2.0 两个版本的朋友要小心啊!

您的项目是不是时不时会出现 Server Application Unavailable 错误呢?或者一直连续都出这个错误!

今天帮一个老师升级一个web的心理测评系统.那个系统原来是ASP.NET 1.1的,新版本是ASP.NET 2.0的..(PS:那台服务器上面还装其他的ASP.NET程序,用的是ASP.NET1.1的.)

1、在服务器上创建一个目录,然后将发布后的所有Web程序的文件复制到这个新建的目录。
2、在服务器的IIS上新建一个虚拟目录,然后 将其配置成可以执行脚本的应用程序,(过程与创建.net Framework 1.1版本的程序相同。) 然后,将此虚拟目录的.net Framework 配置成2.0。
大功告成后,开始访问刚配置的程序,却得到如下错误:
Server Application Unavailable
The web application you are attempting to access on this web server is currently unavailable. Please hit the “Refresh” button in your web browser to retry your request.

Administrator Note: An error message detailing the cause of this specific request failure can be found in the application event log of the web server. Please review this log entry to discover what caused this error to occur.

经过仔细阅读了IIS帮助文档,才发现,原来这个错误是由 IIS6 应用程序隔离机制造成的。IIS默认的应用程序隔离机制被称为:“工作进程隔离模式”,在此模式中,应用程序被分为多个组,每个组就是一个“应用程序池” ,每个应用程序池之间是相互隔离的。 隔离的好处当然就是安全啦,稳定啦,等等。IIS中的每个应用程序池由一个“工作进程”分别进行管理,也就是”W3wp.exe” 。如果有多个应用程序池中的程序运行,我们就能看到多个w3wp.exe。 我们平时新建的虚拟目录都默认被指向IIS6的“DefaultAppPool” 中, 所以在默认情况下,不管你有多少个asp.net程序在运行,在“Windows任务管理器”中你只能看到一个w3wp.exe进程。

出现上述错误的原因是: .net Framework 2.0的程序与.net Framework1.1(或1.0)的程序被放入同一个应用程序池(默认情况下放入DefaultAppPool池),也就是由同一个工作进程: w3wp.exe 进行管理,而单个工作进程是无法同时管理不同的程序(或者不同版本的程序)的。如果先访问.net framework 1.1的页面,则工作进程先加载并管理了 1.1版本的程序集,此时访问.net framework 2.0的web程序页面,Server Application Unavailable 错误就出来了。 反之,如果在默认应用程序池的w3wp.exe尚未启动前先访问了 2.0的web程序(此时应用程序集已经加载了.net framework2.0的Web程序集),再访问1.1或1.0的Web程序页面时,同样会出现“服务器应用程序不可用” 这样的中文提示。(您可以结束掉以前的w3wp.exe进行测试。)

解决办法: 在IIS中新建一个应用程序池,叫”ASP.NET2.0″吧,然后选中那个系统的虚拟目录,点“属性”-》在“应用程序池” 中选择刚才新建的的应用程序池,点击“确定”。

转自:

http://hi.baidu.com/wuwenlin/blog/item/fd6fdff245083615b07ec529.html

sql server 2005 分页

今天上csdn,看到有人在炫耀自己所谓的分页算法,呃。。。我觉得算不上是什么算法,就是一种解决方案吧。不过回帖的人好多,在回帖里淘到了不错的东西,发上来记录一下。

在windows server 2003, sql server 2005 CTP,P4 2.66GHZ,1GB 内存下测试,执行时间0秒 ,表中数据量230万

接下来大批量的数据查询性能瓶颈就在count了,不知道sql server 2005在这方面有没有什么改进.

附另一种方法:

生活日志,始篇

打算在这里一天写一篇生活方面的日志,记录一下自己的成长历程。
我太懒了,懒得写日志,停下来的时候,总是玩玩游戏,时间就消磨过去了,才发现时间是那么的宝贵。
刚刚接到老师的电话,说19日,要交毕业论文的初稿了,可是我一个字都还没有动,看来明天要辛苦了。
毕业设计也还没有最后整合好,毕业论文更不用谈。今天来叔叔家了,没有办法做这些事情,决定明天把本本带到公司去。

据说有Fans要来看我的生活日志,就随手写一篇哈。

C#的疑问

一个C#睡前故事

从 前,在南方一块奇异的土地上,有个工人名叫彼得,他非常勤奋,对他的老板总是百依百顺。但是他的老板是个吝啬的人,从不信任别人,坚决要求随时知道彼得的 工作进度,以防止他偷懒。但是彼得又不想让老板呆在他的办公室里站在背后盯着他,于是就对老板做出承诺:无论何时,只要我的工作取得了一点进展我都会及时 让你知道。彼得通过周期性地使用“带类型的引用”(原文为:“typed reference” 也就是delegate??)“回调”他的老板来实现他的承诺,如下:

接口

现在,彼得成了一个特殊的人,他不但能容忍吝啬的老板,而且和他周围的宇宙也有了密切的联系,以至于他认为宇宙对他的工作进度也感兴趣。不幸的是,他必须也给宇宙添加一个特殊的回调函数Advise来实现同时向他老板和宇宙报告工作进度。彼得想要把潜在的通知的列表和这些通知的实现方法分离开来,于是他决定把方法分离为一个接口:

委托

不幸的是,每当彼得忙于通过接口的实现和老板交流时,就没有机会及时通知宇宙了。至少他应该忽略身在远方的老板的引用,好让其他实现了IWorkerEvents的对象得到他的工作报告。(”At least he’d abstracted the reference of his boss far away from him so that others who implemented the IWorkerEvents interface could be notified of his work progress” 原话如此,不理解到底是什么意思:))

他的老板还是抱怨得很厉害。“彼得!”他老板吼道,“你为什么在工作一开始和工作进行中都来烦我?!我不关心这些事件。你不但强迫我实现了这些方法,而且还在浪费我宝贵的工作时间来处理你的事件,特别是当我外出的时候更是如此!你能不能不再来烦我?”

于是,彼得意识到接口虽然在很多情况都很有用,但是当用作事件时,“粒度”不够好。他希望能够仅在别人想要时才通知他们,于是他决定把接口的方法分离为单独的委托,每个委托都像一个小的接口方法:

静态监听者

这样,彼得不会再拿他老板不想要的事件来烦他老板了,但是他还没有把宇宙放到他的监听者列表中。因为宇宙是个包涵一切的实体,看来不适合使用实例方法的委托(想像一下,实例化一个“宇宙”要花费多少资源…..),于是彼得就需要能够对静态委托进行挂钩,委托对这一点支持得很好:

事件

不幸的是,宇宙太忙了,也不习惯时刻关注它里面的个体,它可以用自己的委托替换了彼得老板的委托。这是把彼得的Worker类的的委托字段做成public的一个无意识的副作用。同样,如果彼得的老板不耐烦了,也可以决定自己来激发彼得的委托(真是一个粗鲁的老板):

彼得不想让这些事发生,他意识到需要给每个委托提供“注册”和“反注册”功能,这样监听者就可以自己添加和移除委托,但同时又不能清空整个列表也不能随意激发彼得的事件了。彼得并没有来自己实现这些功能,相反,他使用了event关键字让C#编译器为他构建这些方法:

彼得知道event关键字在委托的外边包装了一个property,仅让C#客户通过+= 和 -=操作符来添加和移除,强迫他的老板和宇宙正确地使用事件。

收获所有结果

到这时,彼得终于可以送一口气了,他成功地满足了所有监听者的需求,同时避免了与特定实现的紧耦合。但是他注意到他的老板和宇宙都为它的工作打了分,但是他仅仅接收了一个分数。面对多个监听者,他想要“收获”所有的结果,于是他深入到代理里面,轮询监听者列表,手工一个个调用:

异步通知:激发 & 忘掉

同时,他的老板和宇宙还要忙于处理其他事情,也就是说他们给彼得打分所花费的事件变得非常长:

很不幸,彼得每次通知一个监听者后必须等待它给自己打分,现在这些通知花费了他太多的工作事件。于是他决定忘掉分数,仅仅异步激发事件:

异步通知:轮询

这使得彼得可以通知他的监听者,然后立即返回工作,让进程的线程池来调用这些代理。随着时间的过去,彼得发现他丢失了他工作的反馈,他知道听取别人的赞扬和努力工作一样重要,于是他异步激发事件,但是周期性地轮询,取得可用的分数。

异步通知:委托

不幸地,彼得有回到了一开始就想避免的情况中来,比如,老板站在背后盯着他工作。于是,他决定使用自己的委托作为他调用的异步委托完成的通知,让他自己立即回到工作,但是仍可以在别人给他的工作打分后得到通知:

宇宙中的幸福

彼得、他的老板和宇宙最终都满足了。彼得的老板和宇宙可以收到他们感兴趣的事件通知,减少了实现的负担和非必需的往返“差旅费”。彼得可以通知他们,而不管他们要花多长时间来从目的方法中返回,同时又可以异步地得到他的结果。彼得知道,这并不*十分*简单,因为当他异步激发事件时,方法要在另外一个线程中执行,彼得的目的方法完成的通知也是一样的道理。但是,迈克和彼得是好朋友,他很熟悉线程的事情,可以在这个领域提供指导。

他们永远幸福地生活下去……<完>

深入sql server中的事务

. 概述1

. 并发访问不利影响1

1. 脏读(dirty read1

2. 不可重复读(nonrepeatable read1

3. 幻读(phantom read1

. 并发访问控制机制2

1. 2

2. 行版本控制2

. 隔离级别2

. 事务3

1. 事务的模式3

1.1. 显式事务Explicit Transactions3

1.2. 自动提交事务Autocommit Transactions4

1.3. 隐式事务Implicit Transactions4

2. 事务的编程5

2.1. Transact-SQL脚本5

2.2. ADO.NET应用程序接口5

一. 概述

当多个用户同时访问数据库的同一资源时,叫做并发访问。如果并发的访问中有用户对数据进行修改,很可能就会对其他访问同一资源的用户产生不利的影响。可能产生的并发不利影响有以下几类:脏读、不可重复读和幻读。

为了避免并发访问产生的不利影响,sql server设计有两种并发访问的控制机制:锁、行版本控制。

二. 并发访问的不利影响

并发访问,如果没有并发访问控制机制,可能产生的不利影响有以下几种

1. 脏读(dirty read

如果一个用户在更新一条记录,这时第二个用户来读取这条更新了的记录,但是第一个用户在更新了记录后又反悔了,不修改了,回滚了刚才的 更新。这样,导致了第二个用户实际上读取到了一条根本就没有存在过的修改后的记录。如果第一个用户在修改记录期间,把所修改的记录锁住,在修改完成前别的 用户读取不到记录,就能避免这种情况。

2. 不可重复读(nonrepeatable read

第一个用户在一次事务中读取同一记录两次,第一次读取一条记录后,又有第二个用户来访问这条记录,并修改了这条记录,第一个用户第二次 读取这条记录时,得到的是跟第一次不同的数据了。如果第一个用户在两次读取之间锁住要读取的记录,别的用户不能去修改相应的记录就能避免这种情况。

3. 幻读(phantom read

第一个用户在一次事务中两次读取同样满足条件的一批记录,第一次读取一批记录后,又有第二个用户来访问这个表,并在这个表中插入或者删 除了一些记录,第一个用户第二次以同样条件读取这批记录时,可能得到的结果有些记录是在第一次读取时有,第二次的结果中没有了,或者是第二次读取的结果中 有的记录在第一次读取的结果中没有的。如果第一个用户在两次读取之间锁住要读取的记录,别的用户不能去修改相应的记录,也不能增删记录,就能避免这种情 况。

三. 并发访问的控制机制

Sql server中提供了两种并发控制的机制以避免在并发访问时可能产生的不利影响。这两种机制是:

1.

每个事务对所依赖的资源(如行、页或表)请求不同类型的锁。锁可以阻止其他事务以某种可能会导致事务请求锁出错的方式修改资源。当事务不再依赖锁定的资源时,它将释放锁。

根据需要锁定资源的粒度和层次,锁有许多类型,主要的有几种:

表类型:锁定整个表

行类型:锁定某个行

文件类型:锁定某个数据库文件

数据库类型:锁定整个数据库

页类型:锁定8K为单位的数据库页

锁的粒度越小,锁定的范围越小,对别的访问的阻塞就越小,但是所用的锁可能会比较多,锁的消耗就比较大。锁的粒度越大,对别的访问的阻塞可能性就越大,但是所用的锁就会比较少,锁的消耗就比较小。

对于编程人员来说,不用手工去设置控制锁,sql server通过设置事务的隔离级别自动管理锁的设置和控制。

Sql server专门管理锁的是锁管理器,锁管理器通过查询分析器分析待执行的sql语句,来判断语句将会访问哪些资源,进行什么操作,然后结合设定的隔离级别自动分配管理需要用到的锁。

2. 行版本控制

当启用了基于行版本控制的隔离级别时,数据库引擎 将维护修改的每一行的版本。应用程序可以指定事务使用行版本查看事务或查询开始时存在的数据,而不是使用锁保护所有读取。通过使用行版本控制,读取操作阻止其他事务的可能性将大大降低。

四. 隔离级别

上面提到了,sql server通过设置隔离级别来控制锁的使用,从而实现并发法访问控制。

Microsoft SQL Server 数据库引擎支持所有这些隔离级别:

l 未提交读(隔离事务的最低级别,只能保证不读取物理上损坏的数据)

l 已提交读(数据库引擎的默认级别)

l 可重复读

l 可序列化(隔离事务的最高级别,事务之间完全隔离)

这几种隔离级别,对应上面三种并发访问可能产生的不利影响,分别有不同的效果,见下表:

隔离级别

脏读

不可重复读

幻读

未提交读

已提交读

可重复读

快照

可序列化

五. 事务

事务是一个逻辑上的单个的工作单元,其中可以包括许多操作,但是它们在逻辑上是一个整体,要么全部完成,要么全部失败,就好像什么操作都没进行似的。

事务是十分可靠坚固的机制,它能保证事务要么全部完成,要么能全部回滚。

l 锁:使用锁的机制尽可能的保证并发事务的隔离性,避免并发的不利影响。

l 事务日志:事务日志记录着整个事务的所有操作步骤,必要的时候靠日志重新开始事务或者回滚事务。不管出现什么状况,哪怕是网络中断,机器断电,甚至是数据库引擎本身出问题了,通过事务日志都能保证事务的完整性。

l 事务管理:保证一个事务的原子性和数据的一致性。一个事务开始后,它要么成功的完成,要么失败,回滚到事务没开始前的那个状态,事务开始做的所有修改都将复原。

1. 事务的模式

控制事务的开始结束的时间点和事务的范围,有几种事务模式:

1.1.显式事务(Explicit Transactions

显式事务通过sql脚本的BEGIN TRANSACTION或者编程接口(API)的开始事务语句启动事务,以sql脚本的COMMIT ROLLBACK语句提交或回滚事务,编程接口(API)的提交事务或回滚事务语句结束事务。都是通过显式的命令控制事务的开始和结束。

从事务开始到事务提交或者回滚是一个完整的事务周期,事务一旦开始,结果要么是提交,要么是回滚。

如果事务范围内发生错误,错误分为几种类型,不同类型的错误有不同的行为。

l 严重错误

比如,客户端到服务端的网络中断了,或者客户的机器被关机了,数据引擎会被通知数据连接已中断,这样严重的错误数据引擎会自动在服务端回滚整个事务。

l 运行时错误

语句之间的“GO”命令形成的区域为命令批次。数据引擎编译和执行语句是以批次为单位的。一次编译一个批次的命令,编译完成后执行这个批次的命令。存储过程是整个被一次编译的,所以一个存储过程内不分批次,整个过程就是一个批次。

大多数情况下,在一个批次中一条语句发生运行时错误,这个语句将被中止,同时同一批次的所有后续语句也不再执行,但同一批次前面已经执行的命令依然有效。但是可以使用了try…catch捕获错误,并进行相应处理,比如执行事务回滚命令。

有一些运行时错误,比如插入了一个主键重复的记录,只中止当前出错的这条语句,后续的语句照样继续执行。这类错误也能被try…catch捕获到。

为了保证整个事务中,任何语句出现错误都回滚整个事务,最简单的方法是在事务开始前设置SET XACT_ABORT ON,这个设置指示数据引擎,在一个事务中遇到一个错误后,不再执行后续的事务,并回滚整个事务。

l 编译错误

遇到编译错误时,错误语句所在的批次不被执行,并不会受SET XACT_ABORT设置的影响。

1.2.自动提交事务(Autocommit Transactions

这个模式是数据引擎的缺省模式,也是各种编程接口的事务缺省模式。每个单独的语句在完成后被提交,失败后被回滚,编程人员不需要指定任何命令。

每个单独的语句就是一个事务的单位,成功了就提交,这句语句执行错误就回滚这条语句,对其他语句的执行不产生影响。注意这里说的执行错误是运行时错误,如果语句本身有编译错误,比如sql语句的关键词拼写错误了,那么发生编译错误语句所在的那个批次的语句都将不被执行。比如:

USE AdventureWorks;

GO

CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3));

GO

INSERT INTO TestBatch VALUES (1, ‘aaa’);

INSERT INTO TestBatch VALUES (2, ‘bbb’);

INSERT INTO TestBatch VALUSE (3, ‘ccc’); — Syntax error.

GO

SELECT * FROM TestBatch; — Returns no rows.

GO

上面这段sql中的第三个insert语句values关键字拼写错误,将导致编译错误,结果是跟这个语句在同一批次的所有三条insert语句都将不被执行。

如果上面第三个insert语句是这样的:

INSERT INTO TestBatch VALUES (1, ‘ccc’); — Duplicate key error.

这将产生一个运行时错误“重复的主键”,这条语句将被回滚,但是不影响前面两条insert语句。从这点可以看出,自动提交模式是每条单独的语句要么完成要么回滚,不影响其他语句的执行。

1.3.隐式事务(Implicit Transactions

SET IMPLICIT_TRANSACTIONS ON命令之后的第一条语句开始,就开始一个新的事务,直到遇到COMMIT ROLLBACK语句结束这个事务,下一个语句又是一个新的事务,同样直到遇到COMMIT ROLLBACK语句结束这个事务。这样形成了一个事务链,直到SET IMPLICIT_TRANSACTIONS OFF结束隐式事务,回到默认的自动提交事务模式。

事务中的行为跟显式事务模式是一致的。

事务体现在connection的水平,一个connection具有事务模式,自动提交模式是connection的缺省事务模式,直到BEGIN TRANSACTION语句开始显式事务模式,或者隐式事务被SET IMPLICIT_TRANSACTIONS ON设置,连接的事务模式被置为显式或隐式事务模式,当显示事务被提交或者回滚,隐式事务被置为关闭后,这个连接的事务模式又被置为自动提交模式。

2. 事务的编程

数据库的编程有两种方式,一种应用程序接口(API),包括ODBCADO ado.net等等编程接口,一种是Transact-SQL脚本,典型的是存储过程。

2.1.Transact-SQL脚本

BEGIN TRANSACTION

标记显式连接事务的起始点。

COMMIT TRANSACTION COMMIT WORK

如果没有遇到错误,可使用该语句成功地结束事务。该事务中的所有数据修改在数据库中都将永久有效。事务占用的资源将被释放。

ROLLBACK TRANSACTION ROLLBACK WORK

用来回滚遇到错误的事务。该事务修改的所有数据都返回到事务开始时的状态。事务占用的资源将被释放。

2.2.ADO.NET应用程序接口

SqlConnection 对象使用 BeginTransaction 方法可以启动一个显式事务。若要结束事务,可以对 SqlTransaction 对象调用 Commit() Rollback() 方法。

下面主要以在存储过程中使用事务的编程详加说明

使用事务的目的是保持一段sql语句执行的完整性,要么全部执行成功,只要有一条语句失败就能完全回滚,回到事务开始前的状态。

事务有起点,即通过BEGIN TRANSACTION启动一个事务,其后执行事务中的各个语句,最后要判断,全部语句执行都成功了,就用COMMIT TRANSACTION提交事务,把事务中执行的语句的结果固定下来;如果事务中有任何错误,要能捕获到错误,并执行ROLLBACK TRANSACTION回滚整个事务。

下面是一段示例代码:

USE AdventureWorks;

BEGIN TRANSACTION;

BEGIN TRY

产生一个违反约束的错误.

DELETE FROM Production.Product

WHERE ProductID = 980;

END TRY

BEGIN CATCH

SELECT

ERROR_NUMBER() AS ErrorNumber,

ERROR_SEVERITY() AS ErrorSeverity,

ERROR_STATE() as ErrorState,

ERROR_PROCEDURE() as ErrorProcedure,

ERROR_LINE() as ErrorLine,

ERROR_MESSAGE() as ErrorMessage;

IF @@TRANCOUNT > 0

ROLLBACK TRANSACTION;

END CATCH;

IF @@TRANCOUNT > 0

COMMIT TRANSACTION;

把事务中要执行的语句都放在TRY语句块中,保证所有语句产生错误都能被捕获到。如果事务中的语句一旦产生错误,事务中的后续语句不再被执行,直接跳到CATCH语句块执行,进行出错后的后续处理过程。

CATCH语句块中的最主要的工作就是执行事务回滚语句,以回滚整个事务。也可以进行一些其他辅助性的工作,显示错误,记录错误等等。

如果事务中所有语句都没有出错,顺利执行完成,程序就跳过CATCH语句块,执行最后的COMMIT TRANSACTION提交事务。

经常看到有些人使用@@error来捕获错误,判断是否需要回滚事务,代码大概如下:

BEGIN TRANSACTION;

Select xxx from yyyy; 事务中的sql语句

……

If @@error > 0

ROLLBACK TRANSACTION;

Else

COMMIT TRANSACTION;

这里使用@@error来判断事务中所有的语句是否发生错误,并以此来决定是回滚事务,还是提交事务。实际上这么做是是十分错误的。

第一,@@error是针对每个sql语句执行结果,反映的是当前执行的语句出错状态,当执行到下一句,@@error又被重置以反应下一句语句的执行结果。所以用@@error来判断所有语句是否出错是不行的。

第二,sql语句的运行时错误有两类,一类是语句发生了错误,此语句被中止,但后续语句还能被继续执行,一类是语句发生错误后,一个命令批次中的后续的所有语句也不再被执行。当事务中的语句发生这种错误,那么放在最后的If @@error > 0判断语句都不会有机会被执行了。

这样的做法可能导致很严重的后果:如果事务中有语句产生第一类的错误,后续语句都不被执行,原来设计的ROLLBACK TRANSACTION或COMMIT TRANSACTION都没有机会被执行,就是说被这个事务锁了的资源都将得不到释放,产生的后果是,如果这个事务对某些记录设置了共享锁,那这些记录再 也不能被修改,更惨的是如果这个事务对某些记录设置了排他锁,那么读取这些记录的语句一直会被堵塞,执行不下去了。程序也就死在那里了。

所以,在事务中用来捕获语句错误还是需要使用try…catch语句块。