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.


Wednesday, 28 October 2009

Javascript in Flash

Imaging an situation when you need to track redirect activity from the flash animation. Execute some tracking script right before user left the flash animation.

Basically there are two ways have you can call javascript from ActionScript:

1) getURL()
2) ExternalInterface.call

Problem is that browsers does not behaves same way.
getURL is properly executed on FF but not in IE7.
ExternalInterface.call is working on FF, IE in same way. Browser executes javascript before redirect.

Simple validation in Model View Presenter

Last time I wrote about how to implement simple page by Model View Presenter pattern. Quickly we created form with 2 textboxes, label and button for firing presenter method SaveData.

This time let's focus a bit to validation in presenter and co-working your page with validation result.

Let's imagine we have form for validating user contact information.

I will assume that you will have presenter in separate assembly. The assembly it self will contains interface of view (used for communication between presenter and page) and presenter logic (presenter logic and interface of presenter).

1) create new assembly, call it for example WebAppPresenter. Create new interface for View

namespace WebAppPresenter
{
public interface IDataView
{
string FirstName { get; }
string LastName { get; }
string EmailAddress { get; }
string Status { set; }
}
}

Another one for interface of presenter


namespace WebAppPresenter
{
public interface IDataPresenter
{
void SaveData();
bool AreDataValid();
}
}

2) the Presenter will implement IDataPresenter inferface created above.


using System;
using System.Text.RegularExpressions;

namespace WebAppPresenter
{
public class DataPresenter : IDataPresenter
{
private IDataView _view;
public DataPresenter(IDataView view)
{
_view = view;
}

public void SaveData()
{
if (AreDataValid()) { _view.Status = "Data could be saved"; }
}

public bool AreDataValid()
{
if (String.IsNullOrEmpty(_view.FirstName))
{ _view.Status = "First Name cannot be blank"; return false;}
if (String.IsNullOrEmpty(_view.LastName))
{ _view.Status = "Last Name cannot be blank"; return false; }
if (!String.IsNullOrEmpty(_view.EmailAddress))
{
if (!Regex.IsMatch(_view.EmailAddress,@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"))
{_view.Status="Email address is not valid!"; return false;}
}
else { _view.Status = "Email address is not valid!"; return false; }

return true;
}

}
}

Let's stop for a while to describe what is happening here. Constructor populating user entries from webpage through IDataView interface. Method SaveData executes validations and set Status based on validation results. The AreDataValid method is the one which is responsible for validation within presenter. It goes through user inputs present in property _view, validating each value and if one of the field is invalid set Status with proper error message.

Because AreDataValid method is present in interface and it's public can be called directly from webpage for example to prevent execution of other code (such SaveData() ) once the input data are not in good shape. It's up to you whether you make validation method visible outside presenter.

3) Now let's make user interaction with presenter. So firstly create some form for getting information from visitors: (extract from the page)


    <form id="frmContact" runat="server">
First name:<asp:TextBox ID="tbFirstName" runat="server" /><br />
Last name:<asp:TextBox ID="tbLastName" runat="server" /><br />
Email Address:<asp:TextBox ID="tbEmailAddress" runat="server" /><br /><br />
Result:<asp:Label ID="lblStatus" runat="server" /><br />
<
asp:Button ID="btnSave" runat="server" Text="Submit" />
</
form>

4) Code behind of the page. Firstly do not forget to inherit from the View (in my scenario it should be IDataView) to be able make connection between Presenter and View layer.


public partial class MyPage : System.Web.UI.Page,IDataView
{
#region IDataView Members
public string FirstName { get { return this.tbFirstName.Text; } }
public string LastName { get { return this.tbLastName.Text; } }
public string EmailAddress { get { return this.tbEmailAddress.Text; } }
public string Status { set { this.lblStatus.Text = value; } }
#endregion

protected void
Page_Load(object sender, EventArgs e)
{ }
}

I inherited from IDataView to be able implement it's members and connect them with the webform

5) In Page_Load() method is good to connect to the Presenter and initialize it.


protected void Page_Load(object sender, EventArgs e)
{
IDataPresenter _presenter = new DataPresenter(this);
}


Notice that Presenter constructor had one parameter in its definition and that parameter is the View. So when I initializing presenter I need to populate it with the View. In my scenario View will be webform it self because it inherits from IDataView, so I can use this object.

6) Now just need to wire up button btnSave click event to launch SaveData method from Presenter.


protected void Page_Load(object sender, EventArgs e)
{
IDataPresenter _presenter = new DataPresenter(this);
btnSave.Click += delegate{ _presenter.SaveData(); };
}


As I mentioned above you can also call the _presenter.AreDataValid() first and then based on the result call _presenter.SaveData()

Wednesday, 12 August 2009

Silverlight 3 and Web Service

The most effective way for a Silverlight application to tap into server-side code is through web services. The basic idea is simple—you include a web service with your ASP.NET website or add reference to a standalone web service, and your Silverlight application calls the methods in that web service. Silverlight applications can call traditional ASP.NET web services (.asmx services) as well as the WCF services, which are the newer standard.

Creating a Web Service


There are 2 ways how to create a Web Service in Visual Studio:
  1. Add New WCF Service Application Project (File - New Project - WCF Service Application)
  2. Add New Item - Silverlight-Enabled WCF Service (In the Solution Explorer on your web application project right click and select from the content menu Add - New Item - Silverlight-Enabled WCF Service
As I mentioned in my previous post, if you're about to create a standalone WCF Service Application you'll also needed to create and copy to your web root directory clientAccessPolicy.xml and crossDomain.xml files. These two files take care about accessibility of the Web Service.

When the project "included" WCF Service is created, you don't need to think about accessibility files. Visual Studio automatically configures the Web Service to be accessible for the Silverlight application.

When you create a web service, Visual Studio, creates an Interface and Implementation files, namely as defaults: IService1.vb and Service1.svc.

All functions/subs definitions you create in the Interface file (IService1.vb) - make sure you Imports System.ServiceModel - have to start with <OperationContract()>
WCF Service doesn't support all object types to return results from functions, expect of some defaults such as Integer, String. Non supported object have to be implemented via class. Each class definition has to start with <DataContract()> and all property within a class has to start with <DataMember()>

Example of IService1.vb
Imports System.ServiceModel
<ServiceContract()> _
Public Interface IService1
<OperationContract()> _
Function GetUsers() As List(Of user)
End Interface
<DataContract()> _
Public Class user
Private _uId As Integer
Private _firstName As String
Private _lastName As String
<DataMember()> _
Public Property uId() As Integer
Get
Return _uId
End Get
Set(ByVal value As Integer)
_uId = value
End Set
End Property
<DataMember()> _
Public Property firstName() As String
Get
Return _firstName
End Get
Set(ByVal value As String)
_firstName = value
End Set
End Property
<DataMember()> _
Public Property lastName() As String
Get
Return _lastName
End Get
Set(ByVal value As String)
_lastName = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal usrId As Integer, _
ByVal
uFirstName As String, ByVal uLastName As String)
Me.uId = usrId
Me.firstName = uFirstName
Me.lastName = uLastName
End Sub

Very important

If you'd like to use the web service with your Silverlight application, you must modify the web service's Web.Config file and change service endpoint binding to "basicHttpBinding". That's because the Silverlight works only with this one.

To edit this value, open the web service's Web.Config file and scroll almost at the very end of the file. You have to search for these tags:

<system.serviceModel>
<services>
<service name=...

under the <service> tag you'll find <endpoint address=... tag. Edit this tag's binding value to "basicHttpBinding"

Example - before update:

<system.serviceModel>
<services>
<service name="WcfService.Service1"
behaviorConfiguration="WcfService.Service1Behavior">
<endpoint address="" binding="wsHttpBinding"
contract="WcfService.IService1">

after update:

<system.serviceModel>
<services>
<service name="WcfService.Service1"
behaviorConfiguration="WcfService.Service1Behavior">
<endpoint address="" binding="basicHttpBinding"
contract="WcfService.IService1">

Note: Before you publish your Silverlight application to the IIS, make sure you modified the ServiceReferences.ClientConfig file located in your Silverlight application folder. Only one but most important change is required - modify WCF service's endpoint address to the one on which the WCF service to be accessible on the Internet. By default, Visual Studio set up this address for running the web application in development environment, such as <endpoint address="http://localhost:49249/SilverlightApplication.Web/Service.svc".
Suppose, your WCF service is published on Default Web Site\WCFService folder, so the "real" service address will be http://localhost/WCFService/Service.svc therefore service's endpoint address in ServiceReferences.ClientConfig file should looks like:
<endpoint address="http://localhost/WCFService/Service.svc"
Please refer to Publish Silverlight 3 on IIS 7.0/7.5 for more information.

And finally
Whatever service creation way you choose you'll need to add a web service reference to your Silverlight application.

Adding a web service reference is easy. Just, in the Solution Explorer right click on your Silverlight application and from the content menu select "Add Service Reference". In the opened pop-up window (Add Service Reference) fill-up the "Address" text box with the service URL (if you're about to add reference to a standalone service already published on the Web) or click "Discover" button and Visual Studio automatically finds service(s) from current project for you. You can change the default service namespace in the "Namespace" text box to be more comfortable for you. Click "OK" and it's done.

Please note: if change and/or rebuilt your web service you have to update the web service reference in your Silverlight application. To update a web service reference you have to right click on its name in the Solution Explorer under Service References and select "Update Service Reference" from the offered content menu.

When you add or update a web service reference, Visual Studio, creates some files in Service References folder and ServiceReferences.ClientConfig file in the root of your Silverlight application.

Start consuming the web service

Security

All data sent from web service are sent as clear text. So, if you're sending sensitive data consider using appropriate encryption for your data.

Adding Service Reference

If you'd like to use your WCF Service in your Silverlight application you need to add a Service Reference.
In Visual Studio, in Solution Explorer right click on your Silverlight application folder and select "Add Service Reference" from the context menu. The pop-up appears.
To the Address field enter the WCF service's URL address. If you're adding an "project included" WCF service (your service is in your current solution) you can click to "Discover" button - it will list all found services in the current solution. Choose one you'd like to add.
Change the Namespace field if you'd like to.
Click "OK". If everything went smoothly your WCF service reference is successfully added and you can find the ServiceReferences.ClientConfig file in the application root.

Consuming web service

After you've created the Service Reference you're ready to use/consume the web service in your Silverlight application.
It's useful to import the Service Namespace to your application (Performance tip: every "dot" in your command requires a call to the object and it's dramatically impact on performance.)
And finally you can write "consumer" script.

Example: Suppose, you've created the Web Service. Next step is to add a Service Reference, setup Service Namespace as wcfService. Your Silverlight project's name is SilverlightApplication. First of all, open or create a xaml file which is about to consume your Web Service, import Service Namespace and write "consumer" script (in this example I'll call the Service after page is loaded). Because Service is asynchronious you need to add a Handler to sub what will consume the result from the Web Service. The MainForm.xaml contains <TextBlock x:Name="txtUsers"/>

Imports SilverlightApplication.wcfService

Partial Public Class MainPage
Inherits UserControl

Private Sub MainPage_Loaded(ByVal sender As Object, ByVal e As _
System.Windows.RoutedEventArgs) Handles Me.Loaded
Dim client As New ServiceClient
With client
AddHandler .getUsersCompleted, AddressOf usersComplete
.getUsersAsync()
.CloseAsync()
End With
client = Nothing
End Sub

Private Sub usersComplete(ByVal sender As Object, ByVal e As _
getUsersCompletedEventArgs)
Dim users As New System.Text.StringBuilder
For Each u As usr In e.Result
With users
.Append("uID = " & u.uId.ToString & vbCrLf)
.Append("firstName = " & u.firstName & vbCrLf)
.Appned("lastName = " & u.lastName & vbCrLf)
End With
Next
txtUsers.Text = users.ToString
End Sub

That's all the basic information you need to know about Silverlight ∧ Web Service. For more details see next postings.

Happy coding!

Tuesday, 11 August 2009

Tracking with dcsMultiTrack invalid fields on client side

dcsMultiTrack - is 3rd compoment. Instead of dcsMultiTrack you can use your own tracking tool or for example google analytics.

Sometimes you can find yourself in situation when you want to track if some of the fields on your form is not valid. Track some value when it does not goes through clientside validation. In this situation you need to somehow hook up on or before validation it self will be executed. In my sample I use custom javascript function which executes validation on form fields and based on their validity reporting status of the form.

1) set custom attribute on the field you want to track. For example on textbox with username you can set up attribute DSCError with value "UN".
<asp:textbox id="username" runat="server" DSCError="UN" />

2) place validation control by own choice (regular, required, custom ...) and ensure that clientside validation is turned on.

3) The button used for submit need to have onclick clientside event with own function used for validation. Custom validation function should contain one parameter ValidationGroup (useful for avoid validation of other validationgroups).
For example onclick="PreValidation('MyValidationGroup');"

4) Custom validation function. Function should consume ValidationGroup name but it is not required. If you does not provide it  (PreValidation('') or PreValidation() ) function will validate all fields with validators.

function PreValidation(valGroupName) {
var errorsToReport = "";

for (i = 0; i < Page_Validators.length; i++) {
var val = Page_Validators[i];
// in case there is no value for valGroupName validate all validators
if (typeof (valGroupName) == 'undefined' || valGroupName == '' ||
val.validationGroup == valGroupName)
{
var cntrToValidate = $get(val.controltovalidate);
if (cntrToValidate != null) {
var DSCError = cntrToValidate.attributes["DCSError"];
if (DSCError != null) {
ValidatorValidate(val, val.validationGroup, null);
if (val.isvalid == false) {
errorsToReport += (errorsToReport.length > 1 ? "," : "");
errorsToReport += DSCError.value;
}
}
}
}
}
if (errorsToReport.length > 0) {
dcsMultiTrack("DCS.dcsuri", "/your_page_name", "DCSext.ErrorFields", errorsToReport);
}
}


How does it works? Well firstly we need to go through all validators on the page, they are present in Page_Validators collection. Then check out whether actuall validator could be evaluated or not. If so store field you tracking into variable cntrToValidate. Check present of custom attribute DCSError on the field. Finally execute the validation with ValidatorValidate(...). Based on the results populate errorsToReport with list failed fields DSCError entries. When validation is done, try to track failed fields by calling dcsMultiTrack.