There was a time when all my energies and effort went into building web applications. In the beginning the platform I was on was Microsoft ASP 2.0, but since 2002 it became all about ASP.NET Web Forms.

I still remember clearly the excitement there was around the programming model and architecture brought by Web Forms. The new code-behind style allowed to finally separating a web page’s layout from its code logic. The server controls programming model were built so that developers could build web pages pretending they were Windows Forms.

The promise of Web Forms was that web developers would never have to touch HTML and JavaScript ever again. They could simply have to add a bunch of .NET controls to a class, set a couple of properties and web pages would magically appear in the browser.

Although ASP.NET Web Forms was designed with a noble goal in mind, it turned out the Forms/Controls metaphor never completely worked for the web.

infoSure, the Web Forms model boosted productivity compared to previous technologies like ASP. However it never succeeded in shielding developers from having to deal HTML, CSS and JavaScript. That essentially meant ignoring the very basic elements of the web.

This is a story of how I was painfully reminded of this reality.

ASP.NET and the Ajax Control Toolkit

At some point in time the Web became all about rich user interaction. One way to achieve this was through the power of asynchronous HTTP requests made through JavaScript, which returned XML data. This combination is commonly referred to as Ajax.
When Ajax got widespread popularity, Microsoft built upon the Web Forms model to enable developers to leverage this new programming paradigm. Once more with the promise of ever having to touch a line of JavaScript.AspNetAjaxLogo

This effort culminated in a new infrastructure made available in the .NET Framework 3.5 and a collection of Ajax-enabled server controls. Once dragged into a Web Forms page, these controls would instantly deliver rich functionality by emitting the required JavaScript code to make it happen.

Contrarily to what had been done in the past, the new Ajax controls were not made part of an official version of the .NET Framework, but only the underlying framework support they need in order to work. The control themselves were released as an open-source project called the ASP.NET Ajax Control Toolkit hosted on the Microsoft CodePlex site.

The illusion

After having been away from web development for almost three years, I’ve lately been involved in a project to build a web application. Of course the customer expected a modern and interactive web application, which meant we were going to be using Ajax on the frontend to some extent.

After having brought myself up to speed on the latest innovations around Ajax in ASP.NET 3.5, I was excited at the idea of be able to deliver that kind of functionality on a web page without having to handcraft (and debug) gobs of JavaScript. Or at least, so I thought.

Facing reality

I have to admit that the Ajax support in ASP.NET 3.5 held up to my high expectations quite well. Up until the Web Forms metaphor leaked again and I was roughly brought back to earth.

It turns out the ComboBox control contained in the Ajax Control Toolkit has a nasty bug that manifested itself for me when I used it inside a TabPanel control (also part of the same library).

Here is what happens: the first screenshot shows a ComboBox control inside a TabPanel that is visible when the page loads for the first time. Below is another ComboBox control this time hosted in a second TabPanel that is initially hidden.

ASP.NET Ajax ComboBox control working
ASP.NET Ajax ComboBox control broken

The second definitely doesn’t look right. After a quick check to the documentation available online I couldn’t find anything I was doing wrong when using the controls. The only possible explanation was that there must be a bug in the JavaScript generated by the ComboBox. Let me just check. Yes, here it is.

Apparently there is currently no plan from Microsoft to fix this issue anytime soon. That could mean only one thing: I had to dig in and debug the JavaScript myself. The Web Forms’ bubble had burst once again.

The “pragmatic” workaround

After having downloaded the AJAX Control Toolkit source code off CodePlex, I started to look around among the project files. I quickly indentified that the JavaScript code for the ComboBox control is all contained in a single file found in /AjaxControlToolkit/ComboBox/ComboBox.js (actually the ComboBox.debug.js file contains the original source code while its ComboBox.js counterpart contains the minified JavaScript optimized for production).

The general design of the client-side Ajax framework and controls built by Microsoft makes a lot of sense and the source code is well organized. This allowed me to quickly arrive at the root of the problem, which is:

infoThe ComboBox control calculates its size (width and height) during initialization relatively to the size of its parent container. If the parent container is hidden when it gets measured, the returned size will be zero. That means the ComboBox has nothing to calculate its own site against and it ends up looking the way it does.

Without having to dig too much into the inner workings of the ComboBox, I came up with the simplest possible solution to the problem:

infoWe need to make sure that the ComboBox’s parent container is visible during the control’s initialization phase. That way the ComboBox’s size can correctly be calculated and assigned. Afterwards we can restore the parent container to its original state.

In order to achieve this I added the following code (lines 9-16 and 30-40) to the ComboBox.debug.js file:

AjaxControlToolkit.ComboBox.prototype = {

    initialize: function() {

        AjaxControlToolkit.ComboBox.callBaseMethod(this, 'initialize');

        // Workaround for issue #24251
        // http://ajaxcontroltoolkit.codeplex.com/WorkItem/View.aspx?WorkItemId=24251
        var hiddenParent = this._findHiddenParent(this.get_comboTableControl());
        var hiddenParentDisplay;

        if (hiddenParent != null) {
            hiddenParentDisplay = hiddenParent.style.display;
            hiddenParent.style.visibility = "visible";
            hiddenParent.style.display = "block";
        }

        this.createDelegates();
        this.initializeTextBox();
        this.initializeButton();
        this.initializeOptionList();
        this.addHandlers();

        if (hiddenParent != null) {
            hiddenParent.style.visibility = "hidden";
            hiddenParent.style.display = hiddenParentDisplay;
        }

    },
    _findHiddenParent: function(element) {

        var parent = element.parentElement;

        if (parent == null || parent.style.visibility == "hidden") {
            return parent;
        }

        return this._findHiddenParent(parent);

    }

Yes I know this isn’t the most elegant solution, but it works. After all, I said it was going to be pragmatic.

Once I made sure the patch worked correctly, I used the freely available Microsoft Ajax Minifier to produce a new ultra-compact (or minified) version of the ComboBox.js file.

Integrating the workaround into the solution

The workaround itself may not be a piece of art. However the way it got integrated into the existing ASP.NET web application is quite elegant in my opinion. Let me give a quick background first.

With ASP.NET Web Forms 3.5 Microsoft introduced a new mechanism for delivering JavaScript content into web pages. This is done by a specialized server control called the ScriptManager.

infoAll controls that need some piece of JavaScript code to in order to work, have to register the required scripts with the ScriptManager. Its responsibility is to make sure that the links to the appropriate resources are ultimately included in the page output.

The ScriptManager obviously plays a central role in the ASP.NET Ajax infrastructure. However it has some great features too. In this case  I’m referring to the possibility to substitute a JavaScript resource required by a server control with a local resource. Scott Hanselman wrote a great article explaining how to take advantage of this feature, which served me well in this case.

infoSince all JavaScript files contained in the Ajax Control Toolkit are statically compiled in the AjaxControlToolkit.dll assembly, the only way to replace the original ComboBox.js file with the patched one without having to deploy a recompiled version of the library, was to substitute the original reference within the ScriptManager and have it point to a local version of the file.

Here is how it was done:

<asp:scriptmanager id="scmScriptManager" runat="server">
    <scripts>
        <asp:scriptreference path="~/UI/Scripts/ComboBox.js" name="AjaxControlToolkit.ComboBox.ComboBox.js" assembly="AjaxControlToolkit, Version=3.0.30512.20315, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" />
    </scripts>
</asp:scriptmanager>

What about the original ComboBox.debug.js file? Well, the ScriptManager is smart enough to deliver the appropriate version of the file whenever debugging is enabled in the web application’s configuration file. This will work automatically as long as both files are located in the same folder on the server and are named according to the following convention:

  • Original: filename.debug.js
  • Optimized: filename.js

You can download the modified JavaScript files from the link at the end of this page. Note that they are based on and will work with the Ajax Control Toolkit release 3.0.30512.

Conclusions

The Ajax support built into ASP.NET 3.5 Web Forms together with the control freely available in the Ajax Control Toolkit is a powerful combination. When used wisely it will allow you to get quite far in creating rich and interactive web pages without having to worry about JavaScript.

However we all know that software abstractions are leaky, and this is especially true for the one that is ASP.NET Web Forms. That means that sooner or later you will have to take control of what’s being sent down to the browser, whether it be the HTML markup, CSS stylesheets or JavaScript code. And when that time comes, you’d better be prepared.

Download Download Ajax Control Toolkit ComboBox JavaScript files

/Enrico

Managing state across the HTTP protocol has always been one of the major challenges faced by developers when building applications on the web. Of course web services are no exception.

One way to overcome the stateless nature of HTTP without putting to much load on the web server, is to offload some of the information that has to be saved in the context of a particular conversation over to the client. The HTTP specification provides a native mechanism to do just that, by allowing web servers to bundle small pieces of textual data in a dedicated header of the response messages sent to the clients. These recognize the special payload, extract it, and store it in a local cache on disk to have it ready to be sent with every subsequent request. These small texts are technically known as “cookies”.

Cookies are opaque in ASMX

Cookies go back a long time in the history of HTTP, and have served the Internet (fairly) well so far. Sure they brought some serious security issues with them, but for the most part they have been a conventient way for developers of web sites/web  applications to save temporary pieces of information off the server and have it transparently sent back by the client with each request.
This guarantee comes from the factAsmxWs that every web browser on Earth has had the notion of cookies since web browser have had built-in support for cookies for the last 15 years or so.    

However, when it comes to web services, this assumption is no longer valid, since the client isn’t necessarily a web browser and doesn’t have to know how to handle cookies.

In the ASMX programming model, this problem has a quite simple solution. The client objects used to invoke operations on a web service can optionally reference an instance of a “cookie container”, were all cookies passed back by the web server are automatically stored and sent with each request.

using System.Net;

public class Program
{
  private static void Main(string[] args)
  {
    // Creates a new instance of a client proxy for an ASMX Web service
    MyWebServiceClient client = new MyWebServiceClient();

    // Creates the cookie container and assigns it to the proxy
    CookieContainer cookieJar = new CookieContainer();
    client.CookieContainer = cookieJar;

    // From now on cookies returned by any of the web service operations
    // are automatically handled by the proxy
    client.DoSomething();
  }
}

The advantage with this approach is that it is fairly opaque to the developer, which can inspect the contents of the cookie container at any time. As a bonus, it allows the same cookie container to easily be shared between multiple clients, enabling the scenarios when same cookie is required by multiple web services.

But they are transparent in WCF

In the WCF world, things are a little bit different. WCF, being a transport-agnostic technology, doesn’t allow the concept of a cookie to be directly reflected in the high level API, since it is specific to the HTTP protocol. This translate in practice in the web service client objects not having any CookieContainer property to set and retrieve.

However this isn’t necessarily a problem, since Microsoft did put a the possibility to enable automatic “behind the scenes” cookie management for HTTP clients. This of course is implemented at the WCF binding level, and can be switched on with a configuration setting:

<system.ServiceModel>
    <bindings>
        <basicHttpBinding allowCookies="true">
    </bindings>
    <client>
        <endpoint address="http://localhost/myservice"
                  binding="basicHttpBinding"
                  contract="IMyService" />
    </client>
</system.ServiceModel>

When this option is enabled the client will make sure all cookies received from a given web service are stored and properly sent on each subsequent request in a transparent fashion. But there is a catch: the cookie is only handled in the conversation with one web service. What if you need to send the same cookies to different web services?

Well, you’ll have to explicitly set the EnableCookies setting to false  (kind of counter-intuitive I know, but required nonetheless) and start managing the cookies yourself. Luckily, there are a couple of solutions.

Ad-hoc cookie management

If you wish to manually retrieve, store and send a the same given set of cookies from two different web service client objects in WCF, you could do this ad-hoc this way:

using System.ServiceModel;
using System.ServiceModel.Channels;

public class Program
{
    private static void Main(object[] args)
    {
        string sharedCookie;

        MyWebServiceClient client = new MyWebServiceClient();

        using (new OperationContextScope(client.InnerChannel))
        {
            client.DoSomething();

            // Extract the cookie embedded in the received web service response
            // and stores it locally
            HttpResponseMessageProperty response = (HttpResponseMessageProperty)
            OperationContext.Current.IncomingMessageProperties[
                HttpResponseMessageProperty.Name];
            sharedCookie = response.Headers["Set-Cookie"];
        }

        MyOtherWebServiceClient otherClient = new MyOtherWebServiceClient();

        using (new OperationContextScope(otherClient.InnerChannel))
        {
            // Embeds the extracted cookie in the next web service request
            // Note that we manually have to create the request object since
            // since it doesn't exist yet at this stage 
            HttpRequestMessageProperty request = new HttpRequestMessageProperty();
            request.Headers["Cookie"] = sharedCookie;
            OperationContext.Current.OutgoingMessageProperties[
                HttpRequestMessageProperty.Name] = request;

            otherClient.DoSomethingElse();
        }
    }
}
 
Here we are interacting directly with the HTTP messages exchanged with the web services, reading and writing the cookies as a string in appropriate headers. In order to accomplish the task we need to use the “transport-agnostic” WCF API, which indeed makes the code more verbose compared with the ASMX example.

Centralized cookie management

In situations were cookies must be managed in the same way for all web services invoked from a client applications, your best bet is to opt for a centralized solution by applying a very useful feature in WCF: message inspectors.

Message inspectors provide a hook in the WCF messaging pipeline offering the chance to look at and possibly modify all incoming or outgoing messages that transit on the server-side as well as on the client-side. The inspectors that are registered with the WCF runtime receive the messages before they are passed on to the application or sent to the wire, depending on whether it is an incoming or outgoing message.

WcfMessageInspectors

This way, it is possible to catch all HTTP responses coming from the web server, extract any cookies contained within the messages, and manually inject them in all subsequent HTTP requests on their way out. Here is a simplified view of the solution:

using System.ServiceModel;
using System.ServiceModel.Channels;

public class CookieManagerMessageInspector : IClientMessageInspector
{
    private string sharedCookie;

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        HttpResponseMessageProperty httpResponse =
            reply.Properties[HttpResponseMessageProperty.Name]
            as HttpResponseMessageProperty;

        if (httpResponse != null)
        {
            string cookie = httpResponse.Headers[HttpResponseHeader.SetCookie];

            if (!string.IsNullOrEmpty(cookie))
            {
                this.sharedCookie = cookie;
            }
        }
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        HttpRequestMessageProperty httpRequest;

        // The HTTP request object is made available in the outgoing message only
        // when the Visual Studio Debugger is attacched to the running process
        if (!request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
        {
            request.Properties.Add(HttpRequestMessageProperty.Name,
                new HttpRequestMessageProperty());
        }

        httpRequest = (HttpRequestMessageProperty)
            request.Properties[HttpRequestMessageProperty.Name];
        httpRequest.Headers.Add(HttpRequestHeader.Cookie, this.sharedCookie);

        return null;
    }
}

Message inspectors are enabled through the WCF extensibility mechanism called behaviors for single web service operations, entire web service contracts, or even specific endpoint URLs, depending on the scope the will operate in.

Here you can download a sample application showing how to implement a client-side message inspector to share the same cookies across multiple web services.

Download Download WCF Cookie Manager

/Enrico

I am currently in the middle of a project where we are migrating a (large) amount of web services built on top of ASP.NET in .NET 2.0 (commonly referred to as ASMX Web Services) over to the Windows Communication Foundation (WCF) platform available in .NET 3.0.
The primary reason we want to do that, is because we would like to take advantage of the binary message serialization support available in WCF to speedaspnetlogo up communication  between the services and their clients. This same technique was possible in earlier versions of the .NET Framework thanks to a technology called Remoting, which is now superceded by WCF. In both cases it requires that both the client and server run on .NET in order to work. But I digress.

Since the ASMX model has been for a long time the primary Microsoft technology for building web services on the .NET platform, I figured they must have laid out a nice migration path to bring all those web services to the new world of WCF. It turned out, they did!

The quick way

If you built your ASMX web services with code separation (that is, all programming logic resided in a separate code-behind file instead of being embedded in the ASMX file) it is possible to get an ASMX web service up and running in WCF pretty quickly by going through a few easy steps:

  • Your WCF web service class no longer inherits from
    the  System.Web.Services.WebService class so remove it.
  • Change the System.Web.Services.WebService attribute on the web service class to the System.ServiceModel.ServiceContract attribute.
  • Change the System.Web.Services.WebMethod attribute on each web service method to the System.ServiceModel.OperationContract attribute.
  • Substitute the .ASMX file with a new .SVC file with the following header:
<% @ServiceHost Service="MyNamespace.MyService" %>
  • Modify the application configuration file to create a WCF endpoint that clients will use to send their requests to:
<system.serviceModel>
    <behaviors>
        <serviceBehaviors>
            <behavior name="MetadataEnabled">
                <serviceMetadata httpGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
    <services>
        <service name="MyNamespace.MyService"
                 behaviorConfiguration="MetadataEnabled">
            <endpoint name="HttpEndpoint"
                      address=""
                      binding="wsHttpBinding"
                      contract="MyNamespace.IMyService" />
            <endpoint name="HttpMetadata"
                      address="contract"
                      binding="mexHttpBinding"
                      contract="IMetadataExchange" />
            <host>
                <baseAddresses>
                    <add baseAddress="http://localhost/myservice" />
                </baseAddresses>
            </host>
        </service>
    </services>
</system.serviceModel>
  • Decorate all classes that are exchanged by the web service methods as parameters or return values with the System.RuntimeSerialization.DataContract attribute to allow them to be serialized on the wire.
  • Decorate each field of the data classes with the System.RuntimeSerialization.DataMember attribute to include it in the serialized message.

Here is a summary of the changes you’ll have to make to your ASMX web service:

Where the change applies ASMX WCF
Web service class inheritance WebService -
Web service class attribute WebServiceAttribute ServiceContractAttribute
Web service method attribute WebMethodAttribute OperationContractAttribute
Data class attribute XmlRootAttribute DataContractAttribute
Data class field attribute XmlElementAttribute DataMemberAttribute
HTTP endpoint resource .ASMX .SVC

As a side note, if you are using .NET 3.5 SP1 the porting process gets a little easier, since WCF will automatically serialize any object that is part of a service interface without the need of any special metadata attached to it. This means you no longer have to decorate the classes and members exposed by a WCF service contract with the DataContract and DataMember attributes.

Important considerations

The simple process I just described works well for relatively simple web services, but in any real-world scenario you will have to take into consideration a few number of aspects:

  • WCF services are not bound to the HTTP protocol as ASMX web services are. This means you can host them in any process you like, whether it be a Windows Service or a console application. In other words using Microsoft IIS is no longer a requirement, although it is a valid possibility in many situations.
  • Even if WCF services are executed by the ASP.NET worker process when hosted in IIS, they do not participate in the ASP.NET HTTP Pipeline. This means that you have no longer access to the ASP.NET infrastructure services such as HttpContext, HttpSessionState, HttpApplicationState, ASP.NET Authorization and Impersonation in your WCF services.

So what if your ASMX web services are making extensive use of the ASP.NET session store or employ the ASP.NET security model? Is it a show-stopper?
Luckily enough, no. There is a solution to keep all that ASP.NET goodness working in WCF. It is called ASP.NET Compatibility Mode.

Backwards compatibility

Running WCF services with the ASP.NET Compatibility Mode enabled will integrate them in the ASP.NET HTTP Pipeline, which of course means all ASP.NET infrastructure will be back in place and available from WCF at runtime.
You can enable this mighty mode from WCF by following these steps:

  • Decorate your web service class with the System.ServiceModel.Activation.AspNetCompatibilityRequirements attribute as following:
[AspNetCompatibilityRequirements(
    RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class MyService : IMyService
{
    // Service implementation
}
  • Add the following setting to the application configuration file:
<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>

Remember that this will effectively tie the WCF runtime to the HTTP protocol, just like with ASMX web services, which means it becomes prohibited to add any non-HTTP endpoints for your WCF services.

Good luck and please, let me know your feedback!

/Enrico

Follow

Get every new post delivered to your Inbox.

Join 185 other followers