Thursday 25 February 2010

Migration of ASP.NET app from IIS6 to IIS7 (7.5)

For many of us familiar problem. You developing applications under IIS6 and you're about to move them to IIS7. In previous version of IIS was enough to copy over your files, create app pool and site. IIS7 (7.5) is different in this point.

In IIS6 there was only one way hot to extend server with other features – using ISAPI filters. Whole .NET is lived within one dll aspnet_isapi.dll. If the request was for files with .NET type extensions (such .aspx, .ashx, .axd and so on) your application know them and was able to serve them. If request was for file with extension for example .jpg or other static file, the application was not aware about them. For example this is the reason why URL authorization does not work for static files.

IIS7 offers two modes of work:

  • Classic – In IIS requests are processed by above description. Basically the system is compatible with previous versions of IIS
  • Integrated – the default one. In this mode has IIS privileged position, the HTTP handlers and modules (known from ASP.NET) can be executed directly. They are in line with ISAPI filters and built-in IIS functionality.

HTTP handlers, modules were not changed at all, so you don't need to rewrite or recompile them. But was has been changed is the way how to know IIS about which handler, module to use. Basically this is often problem when app is running properly under IIS6 but won't under IIS7. This is not such difference between IISs but it's big difference between Classic and Integrated mode on application pool. You have two options:

  1. Put your application under Classic managed pool (this is not recommended, use it only in case when other solutions fails)
  2. Change the registration of modules and handlers in web.config file to reflect newest configuration schema.

There is no difference whether you register handler or module under IIS6 or IIS7 Classic Mode. Illustration of its registration in web.config:

<?xml version="1.0"?>
<
configuration>
<
system.web>
<
httpHandlers>
<
add verb="*" path="*My.axd" type="MyHttpHandler"/>
</
httpHandlers>
<
httpModules>
<
add name="MyModule" type="MyHttpModule"/>
</
httpModules>
</
system.web>
</
configuration>
In case of web.config in II7 Integration mode, registration will looks like:

<?xml version="1.0"?>
<
configuration>
<
system.webServer>
<
handlers>
<
add name="NameOfMyHandler" verb="*" path="*My.axd" type="MyHttpHandler"/>
</
handlers>
<
modules>
<
add name="MyModule" type="MyHttpModule" preCondition="managedHandler"/>
</
modules>
</
system.webServer>
</
configuration>
Generally you have to perform these changes:
  1. you need rename httpHandlers to handlers and httpModules to modules.


  2. Handlers has required attribute name, so you have to name them


  3. Modules should have attribute preCondition with value managedHandler. This is optional and depends on behavior of particular module (module will called only in case when its execution will be driven by handler written in .NET).

Changes can be done manually or by command line tool (see bellow).

HTTP modules are called for each request. In case of II6 or Classic mode it means for each request mapped in aspnet_isapi.dll configuration. For integrated mode it means for all request including for static files.

Well sometimes you run into problem when you your app needs to work IIS6 as well IIS7. Problem is once you register handlers and module in system.webServer section you need to remove their registration from system.web section. If the IIS would ignore old registration in system.web section I could be security risk caused by not executed some modules, handlers. Mostly those for authentication and authorization. But there is am option how to avoid this checking and allow to have registrations in both sections. All you need to do is to turn off validation by attribute validateIntegratedModeConfiguration. Using this attribute is not really recommended.


So the universal web.config for both scenarios would looks like:

<?xml version="1.0"?>
<
configuration>
<
system.web>
<
httpHandlers>
<
add verb="*" path="*My.axd" type="MyHttpHandler"/>
</
httpHandlers>
<
httpModules>
<
add name="MyModule" type="MyHttpModule" />
</
httpModules>
</
system.web>
<
system.webServer>
<
validation validateIntegratedModeConfiguration="false"/>
<
handlers>
<
add name="NameOfMyHandler" verb="*" path="*My.axd" type="MyHttpHandler"/>
</
handlers>
<
modules>
<
add name="MyModule" type="MyHttpModule" preCondition="managedHandler"/>
</
modules>
</
system.webServer>
</
configuration>
Instead of modifying web.config manually, you can perform required changes from command line by

%windir%\system32\inetsrv\Appcmd migrate config "<ApplicationPath>"

ApplicationPath is site map name in IIS, for example Default Web Site.

The tool will modify web.config in order to move registration of handlers, modules from system.web to system.webServer section.

Even you've did web.config changes you app can still failing under Integration mode. The most common is "Request is not available in this context" exception. This happens when your implementation is about to access Request object in Application_Start method of global.asax file. Error is due to design change in the II7 Integrated pipeline that makes Request object unavailable in Application_Start. The Classic mode has no problems with it. What you can do about it is to change your app to avoid reference to Request context inside Application_Start or running app under Classic mode (not recommended). More about avoiding reference to Request object you can find on Mike Volodarsky's blog.

Automatically download the latest XAP file

You can find yourself in situation that you want to the client browser automatically download latest .XAP file from ClientBin. It's useful for example, if you regularly changing versions and the client's browser ignores these changes. I figured out that this issue is typical to Google Chrome browser but it can occurs with other browser too.

The silverlight xap is loaded by the browser just like any other server resource, so what you are seeing is the cache keeping the old xap and not refreshing with the new one.
To fix this issue, so "tell" the client's browser to automatically download the latest version of XAP file, there are 2 methods.


One approach you could look at is to use a handler to deliver the XAP file
Using the Xap Handler on the server (asp.net) you can add headers to the request from the browser to instruct it not to cache the xap file - you could add more logic to that so these headers are only added when you want to have the xap refreshed, perhaps based on a value in a config file.
Open your Silverlight application in Visual Studio. Add new Generic Handler file (.ashx) to your web application, name it e.g. "SilverlightHandler.ashx" - place the file to the web root, where your *.aspx files are.

Modify the created handler to look like this one:

<%@ WebHandler Language="VB" Class="SilverlightHandler" %>

Imports System
Imports System.Web

Public Class SilverlightHandler : Implements IHttpHandler

Public Sub ProcessRequest(ByVal context As HttpContext) Implements _
IHttpHandler.ProcessRequest
With context.Response
.Expires = -1000
.BufferOutput = False
.ContentType = "application/x-silverlight-app"
.WriteFile(context.Server.MapPath("ClientBin\SilverlightApp.xap"))
End With
End Sub

Public ReadOnly Property IsReusable() As Boolean Implements _
IHttpHandler.IsReusable
Get
Return False
End Get
End Property

End Class

Note: please change the name of your Silverlight application XAP file, in this case replace SilverlightApp.xap

Then change your Silverlight asp.net control's or object's tag Source value to point to "SilverlightHandler.ashx"
For example your control could look like this:
<object data="data:application/x-silverlight-2,"
type="application/x-silverlight-2" width="100%" height="100%">

<param name="source" value="SilverlightHandler.ashx" />
<param name="onError" value="onSilverlightError" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
</object>
Rebuild your Silverlight application - and that's all.

If you're using a WCF Service with your Silverlight application it can be essential to disable "Reduce XAP size by using application library caching" Silverlight build option.
To do this, in the Solution Explorer right click on your Silverlight application project and select Properties. On the first tab, named "Silverlight", make sure that the "Reduce XAP size by using application library caching" option is unchecked. Save changes and rebuild your application. Now, it should work correctly.


Using this technique will force the client's browser automatically download the latest XAP file. Right now, if you take a tour to your "Internet Temporary Files" folder, you can find, that SilverlightApplication.xap is no longer downloaded, it's replaced by the SilverlightHandler.ashx file which takes care about XAP file handling.

Another approach is when you don't want to change the code

If you don't want to use the xaphandler, a simpler way is to just rename the xap file and then update the Silverlight object tag (or silverlight asp.net control) to point to the new one. The browser will then download this automitically. The page url the silverlight control is hosted in will not have changed so the users will not have to change anything.

If you choose first or second scenario it should solve the browser's caching.

Tuesday 23 February 2010

How to attach ".mdf" to SQL Server Or recover ".ldf" file when is deleted/corrupted

The following article can be also used to "brute" shrink of your database but please be aware and always make a copy of your database before processing. To brute shrink your database, run SQL Server's Enterprise Manager and Detach the database you want to shrink. You can backup and/or pack the database's .mdf and .ldf files. Then you can remove the database's .ldf file. Now, in Enterprise Manager you can try Attach the database by choosing the database's .mdf file and follow these instructions:

1. If the DB shows as suspect in Enterprise Manager and you can see it in sysdatabases goto step 5

2. If the DB does not show in the Enterprise Manager but you have the .mdf file run following: (supposing your database's .mdf file is located on e:\file_name.mdf)

Exec sp_attach_single_file_db @dbname=’databaseName’, @physname=’e:\file_name.mdf

3. If this does not create the .LDF (and it probably won’t) file for you, rename the .mdf file and create a brand-new database the same way the original one was created (the same name, same partitions a.s.o.). Run following to be sure it got created:

use master
go
Select dbid, status, name From dbo.sysdatabases (noLock)

4. Delete the .mdf file you just created and rename back the original one. Restart the SQL Server. The database should show up in Enterprise Manager as SUSPECT.

5. Now, to put the DB into Emergency Bypass Status (32768) run following:

sp_configureallow’, 1

Go
reconfigure with override
update dbo.sysdatabases set status = 32768 where dbid = 9 -- check this dbid of your DB (see step 3)

6. Stop SQL Server – rename log file (you’ll delete it later), start SQL Server

7. To build the log run following:

dbcc rebuild_log(‘database_name’, ‘e:\file_name.ldf’)

8. Put DB back from Emergency Bypass Status (32768) to status 16

update dbo.sysdatabases set status=16 where dbid = 9 -- check this dbid of your DB (see step 3)

9. Clean up

sp_configureallow’, 0
go

reconfigure with override

That's all. Right now, your database should be restored and shrinked to maximum.