Life, Surf, Code and everything in between
White Papers | Free Tools | Products | Message Board | News |

Ads Via DevMavens

No Preview Images in File Open Dialogs on Windows 7



I’ve been updating some file uploader code in my photoalbum today and while I was working with the uploader I noticed that the File Open dialog using Silverlight that handles the file selections didn’t allow me to ever see an image preview for image files. It sure would be nice if I could preview the images I’m about to upload before selecting them from a list. Here’s what my list looked like:

FileOPenDialog

This is the Medium Icon view, but regardless of the views available including Content view only icons are showing up.

Silverlight uses the standard Windows File Open Dialog so it uses all the same settings that apply to Explorer when displaying content. It turns out that the Customization options in particular are the problem here. Specifically the Always show icons, never thumbnails option:

FolderOptions

I had this option checked initially, because it’s one of the defenses against runaway random Explorer views that never stay set at my preferences. Alas, while this setting affects Explorer views apparently it also affects all dialog based views in the same way. Unchecking the option above brings back full thumbnailing for all content and icon views. Here’s the same Medium Icon view after turning the option off:

FileOpenDialogPics

which obviously works a whole lot better for selection of images.

The bummer of this is that it’s not controllable at the dialog level – at least not in Silverlight. Dialogs obviously have different requirements than what you see in Explorer so the global configuration is a bit extreme especially when there are no overrides on the dialog interface. Certainly for Silverlight the ability to have previews is a key feature for many applications since it will be dealing with lots of media content most likely.

Hope this helps somebody out. Thanks to Tim Heuer who helped me track this down on Twitter.



.NET WebRequest.PreAuthenticate – not quite what it sounds like



I’ve run into the  problem a few times now: How to pre-authenticate .NET WebRequest calls doing an HTTP call to the server – essentially send authentication credentials on the very first request instead of waiting for a server challenge first? At first glance this sound like it should be easy: The .NET WebRequest object has a PreAuthenticate property which sounds like it should force authentication credentials to be sent on the first request. Looking at the MSDN example certainly looks like it does:

http://msdn.microsoft.com/en-us/library/system.net.webrequest.preauthenticate.aspx

Unfortunately the MSDN sample is wrong. As is the text of the Help topic which incorrectly leads you to believe that PreAuthenticate… wait for it - pre-authenticates. But it doesn’t allow you to set credentials that are sent on the first request.

What this property actually does is quite different. It doesn’t send credentials on the first request but rather caches the credentials ONCE you have already authenticated once. Http Authentication is based on a challenge response mechanism typically where the client sends a request and the server responds with a 401 header requesting authentication.

So the client sends a request like this:

GET /wconnect/admin/wc.wc?_maintain~ShowStatus HTTP/1.1
Host: rasnote
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,de;q=0.7,en-us;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

and the server responds with:

HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: basic realm=rasnote"
X-AspNet-Version: 2.0.50727
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
WWW-Authenticate: Basic realm="rasnote"
X-Powered-By: ASP.NET
Date: Tue, 27 Oct 2009 00:58:20 GMT
Content-Length: 5163

plus the actual error message body.

The client then is responsible for re-sending the current request with the authentication token information provided (in this case Basic Auth):

GET /wconnect/admin/wc.wc?_maintain~ShowStatus HTTP/1.1
Host: rasnote
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,de;q=0.7,en-us;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: TimeTrakker=2HJ1998WH06696; WebLogCommentUser=Rick Strahl|http://www.west-wind.com/|rstrahl@west-wind.com; WebStoreUser=b8bd0ed9
Authorization: Basic cgsf12aDpkc2ZhZG1zMA==

Once the authorization info is sent the server responds with the actual page result.

Now if you use WebRequest (or WebClient) the default behavior is to re-authenticate on every request that requires authorization. This means if you look in  Fiddler or some other HTTP client Proxy that captures requests you’ll see that each request re-authenticates: Here are two requests fired back to back:

TwoRequests

and you can see the 401 challenge, the 200 response for both requests.

If you watch this same conversation between a browser and a server you’ll notice that the first 401 is also there but the subsequent 401 requests are not present.

WebRequest.PreAuthenticate

And this is precisely what the WebRequest.PreAuthenticate property does: It’s a caching mechanism that caches the connection credentials for a given domain in the active process and resends it on subsequent requests. It does not send credentials on the first request but it will cache credentials on subsequent requests after authentication has succeeded:

string url = "http://rasnote/wconnect/admin/wc.wc?_maintain~ShowStatus";
HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;
req.PreAuthenticate = true;
req.Credentials = new NetworkCredential("rick", "secret", "rasnote");
req.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
req.UserAgent = ": Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)";
WebResponse resp = req.GetResponse();
resp.Close();

req = HttpWebRequest.Create(url) as HttpWebRequest;
req.PreAuthenticate = true;
req.Credentials = new NetworkCredential("rstrahl", "secret", "rasnote");
req.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
req.UserAgent = ": Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)";
resp = req.GetResponse();

which results in the desired sequence:

PreAuthenticated

where only the first request doesn’t send credentials.

This is quite useful as it saves quite a few round trips to the server – bascially it saves one auth request request for every authenticated request you make. In most scenarios I think you’d want to send these credentials this way but one downside to this is that there’s no way to log out the client. Since the client always sends the credentials once authenticated only an explicit operation ON THE SERVER can undo the credentials by forcing another login explicitly (ie. re-challenging with a forced 401 request).

Forcing Basic Authentication Credentials on the first Request

On a few occasions I’ve needed to send credentials on a first request – mainly to some oddball third party Web Services (why you’d want to use Basic Auth on a Web Service is beyond me – don’t ask but it’s not uncommon in my experience). This is true of certain services that are using Basic Authentication (especially some Apache based Web Services) and REQUIRE that the authentication is sent right from the first request. No challenge first. Ugly but there it is.

Now the following works only with Basic Authentication because it’s pretty straight forward to create the Basic Authorization ‘token’ in code since it’s just an unencrypted encoding of the user name and password into base64. As you might guess this is totally unsecure and should only be used when using HTTPS/SSL connections (i’m not in this example so I can capture the Fiddler trace and my local machine doesn’t have a cert installed, but for production apps ALWAYS use SSL with basic auth).

The idea is that you simply add the required Authorization header to the request on your own along with the authorization string that encodes the username and password:

string url = "http://rasnote/wconnect/admin/wc.wc?_maintain~ShowStatus";
HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;

string user = "rick";
string pwd = "secret";
string domain = "www.west-wind.com";

string auth = "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(user + ":" + pwd));
req.PreAuthenticate = true;
req.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
req.Headers.Add("Authorization", auth);
req.UserAgent = ": Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)"; WebResponse resp = req.GetResponse(); resp.Close();

This works and causes the request to immediately send auth information to the server. However, this only works with Basic Auth because you can actually create the authentication credentials easily on the client because it’s essentially clear text. The same doesn’t work for Windows or Digest authentication since you can’t easily create the authentication token on the client and send it to the server.

Another issue with this approach is that PreAuthenticate has no effect when you manually force the authentication. As far as Web Request is concerned it never sent the authentication information so it’s not actually caching the value any longer. If you run 3 requests in a row like this:

        string url = "http://rasnote/wconnect/admin/wc.wc?_maintain~ShowStatus";
        HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;

        string user = "ricks";
        string pwd = "secret";
        string domain = "www.west-wind.com";

        string auth = "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(user + ":" + pwd));
        req.PreAuthenticate = true;
        req.Headers.Add("Authorization", auth);
        req.UserAgent = ": Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)";
        WebResponse resp = req.GetResponse();
        resp.Close();


        req = HttpWebRequest.Create(url) as HttpWebRequest;
        req.PreAuthenticate = true;
        req.Credentials = new NetworkCredential(user, pwd, domain);
        req.UserAgent = ": Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)";
        resp = req.GetResponse();
        resp.Close();

        req = HttpWebRequest.Create(url) as HttpWebRequest;
        req.PreAuthenticate = true;
        req.Credentials = new NetworkCredential(user, pwd, domain);
        req.UserAgent = ": Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)";
        resp = req.GetResponse();

you’ll find the trace looking like this:

Manually

where the first request (the one we explicitly add the header to) authenticates, the second challenges, and any subsequent ones then use the PreAuthenticate credential caching. In effect you’ll end up with one extra 401 request in this scenario, which is still better than 401 challenges on each request.

Getting Access to WebRequest in Classic .NET Web Service Clients

If you’re running a classic .NET Web Service client (non-WCF) one issue with the above is how do you get access to the WebRequest to actually add the custom headers to do the custom Authentication described above? One easy way is to implement a partial class that allows you add headers with something like this:

public partial class TaxService 
{ protected NameValueCollection Headers = new NameValueCollection(); public void AddHttpHeader(string key, string value) { this.Headers.Add(key,value); } public void ClearHttpHeaders() { this.Headers.Clear(); } protected override WebRequest GetWebRequest(Uri uri) { HttpWebRequest request = (HttpWebRequest) base.GetWebRequest(uri); request.Headers.Add(this.Headers); return request; }
}

where TaxService is the name of the .NET generated proxy class. In code you can then call AddHttpHeader() anywhere to add additional headers which are sent as part of the GetWebRequest override. Nice and simple once you know where to hook it.

For WCF there’s a bit more work involved by creating a message extension as described here: http://weblogs.asp.net/avnerk/archive/2006/04/26/Adding-custom-headers-to-every-WCF-call-_2D00_-a-solution.aspx.

FWIW, I think that HTTP header manipulation should be readily available on any HTTP based Web Service client DIRECTLY without having to subclass or implement a special interface hook. But alas a little extra work is required in .NET to make this happen

Not a Common Problem, but when it happens…

This has been one of those issues that is really rare, but it’s bitten me on several occasions when dealing with oddball Web services – a couple of times in my own work interacting with various Web Services and a few times on customer projects that required interaction with credentials-first services. Since the servers determine the protocol, we don’t have a choice but to follow the protocol. Lovely following standards that implementers decide to ignore, isn’t it? :-}



jQuery 1.4 Opacity and IE Filters



Ran into a small problem today with my client side jQuery library after switching to jQuery 1.4. I ran into a problem with a shadow plugin that I use to provide drop shadows for absolute elements – for Mozilla WebKit browsers the –moz-box-shadow and –webkit-box-shadow CSS attributes are used but for IE a manual element is created to provide the shadow that underlays the original element along with a blur filter to provide the fuzziness in the shadow. Some of the key pieces are:

var vis = el.is(":visible");
if (!vis)
    el.show();  // must be visible to get .position

var pos = el.position();
if (typeof shEl.style.filter == "string")
    sh.css("filter", 'progid:DXImageTransform.Microsoft.Blur(makeShadow=true, pixelradius=3, shadowOpacity=' + opt.opacity.toString() + ')');

sh.show()
  .css({ position: "absolute",
      width: el.outerWidth(),
      height: el.outerHeight(),
      opacity: opt.opacity,
      background: opt.color,
      left: pos.left + opt.offset,
      top: pos.top + opt.offset
  });

This has always worked in previous versions of jQuery, but with 1.4 the original filter no longer works. It appears that applying the opacity after the original filter wipes out the original filter. IOW, the opacity filter is not applied incrementally, but absolutely which is a real bummer.

Luckily the workaround is relatively easy by just switching the order in which the opacity and filter are applied. If I apply the blur after the opacity I get my correct behavior back with both opacity:

sh.show()
  .css({ position: "absolute",
      width: el.outerWidth(),
      height: el.outerHeight(),
      opacity: opt.opacity,
      background: opt.color,
      left: pos.left + opt.offset,
      top: pos.top + opt.offset
  });

  if (typeof shEl.style.filter == "string")
      sh.css("filter", 'progid:DXImageTransform.Microsoft.Blur(makeShadow=true, pixelradius=3, shadowOpacity=' + opt.opacity.toString() + ')');

While this works this still causes problems in other areas where opacity is implicitly set in code such as for fade operations or in the case of my shadow component the style/property watcher that keeps the shadow and main object linked. Both of these may set the opacity explicitly and that is still broken as it will effectively kill the blur filter.

This seems like a really strange design decision by the jQuery team, since clearly the jquery css function does the right thing for setting filters. Internally however, the opacity setting doesn’t use .css instead hardcoding the filter which given jQuery’s usual flexibility and smart code seems really inappropriate.

The following is from jQuery.js 1.4:

var style = elem.style || elem, set = value !== undefined;

// IE uses filters for opacity
if ( !jQuery.support.opacity && name === "opacity" ) {
    if ( set ) {
        // IE has trouble with opacity if it does not have layout
        // Force it by setting the zoom level
        style.zoom = 1;

        // Set the alpha filter to set the opacity
        var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
        var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
        style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
    }

    return style.filter && style.filter.indexOf("opacity=") >= 0 ?
        (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
        "";
}

You can see here that the style is explicitly set in code rather than relying on $.css() to assign the value resulting in the old filter getting wiped out.

jQuery 1.32 looks a little different:

// IE uses filters for opacity
if ( !jQuery.support.opacity && name == "opacity" ) {
    if ( set ) {
        // IE has trouble with opacity if it does not have layout
        // Force it by setting the zoom level
        elem.zoom = 1;

        // Set the alpha filter to set the opacity
        elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) +
            (parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
    }

    return elem.filter && elem.filter.indexOf("opacity=") >= 0 ?
        (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '':
        "";
}

Offhand I’m not sure why the latter works better since it too is assigning the filter. However, when checking with the IE script debugger I can see that there are actually a couple of filter tags assigned when using jQuery 1.32 but only one when I use jQuery 1.4.

Note also that the jQuery 1.3 compatibility plugin for jQUery 1.4 doesn’t address this issue either.

Resources



HttpContext.Items and Server.Transfer/Execute



A few days ago my buddy Ben Jones pointed out that he ran into a bug in the ScriptContainer control in the West Wind Web and Ajax Toolkit. The problem was basically that when a Server.Transfer call was applied the script container (and also various ClientScriptProxy script embedding routines) would potentially fail to load up the specified scripts.

It turns out the problem is due to the fact that the various components in the toolkit use request specific singletons via a Current property. I use a static Current property tied to a Context.Items[] entry to handle this type of operation which looks something like this:

/// <summary>
/// Current instance of this class which should always be used to 
/// access this object. There are no public constructors to
/// ensure the reference is used as a Singleton to further
/// ensure that all scripts are written to the same clientscript
/// manager.
/// </summary>        
public static ClientScriptProxy Current
{
    get
    {
        if (HttpContext.Current == null)
            return new ClientScriptProxy();

        ClientScriptProxy proxy = null;
        if (HttpContext.Current.Items.Contains(STR_CONTEXTID))
            proxy = HttpContext.Current.Items[STR_CONTEXTID] as ClientScriptProxy;
        else
        {

            proxy = new ClientScriptProxy();
            HttpContext.Current.Items[STR_CONTEXTID] = proxy;
        }
        
        return proxy;
    }
}

The proxy is attached to a Context.Items[] item which makes the instance Request specific. This works perfectly fine in most situations EXCEPT when you’re dealing with Server.Transfer/Execute requests. Server.Transfer doesn’t cause Context.Items to be cleared so both the current transferred request and the original request’s Context.Items collection apply.

For the ClientScriptProxy this causes a problem because script references are tracked on a per request basis in Context.Items to check for script duplication. Once a script is rendered an ID is written into the Context collection and so considered ‘rendered’:

// No dupes - ref script include only once
if (HttpContext.Current.Items.Contains( STR_SCRIPTITEM_IDENTITIFIER + fileId ) )
    return;

HttpContext.Current.Items.Add(STR_SCRIPTITEM_IDENTITIFIER + fileId, string.Empty);


where the fileId is the script name or unique identifier. The problem is on the Transferred page the item will already exist in Context and so fail to render because it thinks the script has already rendered based on the Context item. Bummer.

The workaround for this is simple once you know what’s going on, but in this case it was a bitch to track down because the context items are used in many places throughout this class. The trick is to determine when a request is transferred and then removing the specific keys.

The first issue is to determine if a script is in a Trransfer or Execute call:

if (HttpContext.Current.CurrentHandler != HttpContext.Current.Handler)

Context.Handler is the original handler and CurrentHandler is the actual currently executing handler that is running when a Transfer/Execute is active. You can also use Context.PreviousHandler to get the last handler and chain through the whole list of handlers applied if Transfer calls are nested (dog help us all for the person debugging that).

For the ClientScriptProxy the full logic to check for a transfer and remove the code looks like this:

/// <summary>
/// Clears all the request specific context items which are script references
/// and the script placement index.
/// </summary>
public void ClearContextItemsOnTransfer()
{
    if (HttpContext.Current != null)
    {
        // Check for Server.Transfer/Execute calls - we need to clear out Context.Items
        if (HttpContext.Current.CurrentHandler != HttpContext.Current.Handler)
        {
            List<string> Keys = HttpContext.Current.Items.Keys.Cast<string>().Where(s => s.StartsWith(STR_SCRIPTITEM_IDENTITIFIER) || s == STR_ScriptResourceIndex).ToList();
            foreach (string key in Keys)
            {
                HttpContext.Current.Items.Remove(key);
            }

        }
    }
}

along with a small update to the Current property getter that sets a global flag to indicate whether the request was transferred:

if (!proxy.IsTransferred && HttpContext.Current.Handler != HttpContext.Current.CurrentHandler)
{
    proxy.ClearContextItemsOnTransfer();
    proxy.IsTransferred = true;
}

return proxy;

I know this is pretty ugly, but it works and it’s actually minimal fuss without affecting the behavior of the rest of the class. Ben had a different solution that involved explicitly clearing out the Context items and replacing the collection with a manually maintained list of items which also works, but required changes through the code to make this work.

In hindsight, it would have been better to use a single object that encapsulates all the ‘persisted’ values and store that object in Context instead of all these individual small morsels. Hindsight is always 20/20 though :-}.

If possible use Page.Items

ClientScriptProxy is a generic component that can be used from anywhere in ASP.NET, so there are various methods that are not Page specific on this component which is why I used Context.Items, rather than the Page.Items collection.Page.Items would be a better choice since it will sidestep the above Server.Transfer nightmares as the Page is reloaded completely and so any new Page gets a new Items collection. No fuss there.

So for the ScriptContainer control, which has to live on the page the behavior is a little different. It is attached to Page.Items (since it’s a control):

/// <summary>
/// Returns a current instance of this control if an instance
/// is already loaded on the page. Otherwise a new instance is
/// created, added to the Form and returned.
/// 
/// It's important this function is not called too early in the
/// page cycle - it should not be called before Page.OnInit().
/// 
/// This property is the preferred way to get a reference to a
/// ScriptContainer control that is either already on a page
/// or needs to be created. Controls in particular should always
/// use this property.
/// </summary>
public static ScriptContainer Current
{
    get
    {
        // We need a context for this to work!
        if (HttpContext.Current == null)
            return null;

        Page page = HttpContext.Current.CurrentHandler as Page;
        if (page == null)
            throw new InvalidOperationException(Resources.ERROR_ScriptContainer_OnlyWorks_With_PageBasedHandlers); 

        ScriptContainer ctl = null;

        // Retrieve the current instance
        ctl = page.Items[STR_CONTEXTID] as ScriptContainer;
        if (ctl != null)
            return ctl;
        
        ctl = new ScriptContainer();        
        page.Form.Controls.Add(ctl);
        return ctl;
    }
}

The biggest issue with this approach is that you have to explicitly retrieve the page in the static Current property. Notice again the use of CurrentHandler (rather than Handler which was my original implementation) to ensure you get the latest page including the one that Server.Transfer fired.

Server.Transfer and Server.Execute are Evil

All that said – this fix is probably for the 2 people who are crazy enough to rely on Server.Transfer/Execute. :-} There are so many weird behavior problems with these commands that I avoid them at all costs. I don’t think I have a single application that uses either of these commands…

Related Resources



Rendering ASP.NET Script References into the Html Header



One thing that I’ve come to appreciate in control development in ASP.NET that use JavaScript is the ability to have more control over script and script include placement than ASP.NET provides natively. Specifically in ASP.NET you can use either the ClientScriptManager or ScriptManager to embed scripts and script references into pages via code.

This works reasonably well, but the script references that get generated are generated into the HTML body and there’s very little operational control for placement of scripts. If you have multiple controls or several of the same control that need to place the same scripts onto the page it’s not difficult to end up with scripts that render in the wrong order and stop working correctly. This is especially critical if you load script libraries with dependencies either via resources or even if you are rendering referenced to CDN resources.

Natively ASP.NET provides a host of methods that help embedding scripts into the page via either Page.ClientScript or the ASP.NET ScriptManager control (both with slightly different syntax):

  • RegisterClientScriptBlock
    Renders a script block at the top of the HTML body and should be used for embedding callable functions/classes.
  • RegisterStartupScript
    Renders a script block just prior to the </form> tag and should be used to for embedding code that should execute when the page is first loaded. Not recommended – use jQuery.ready() or equivalent load time routines.
  • RegisterClientScriptInclude
    Embeds a reference to a script from a url into the page.
  • RegisterClientScriptResource
    Embeds a reference to a Script from a resource file generating a long resource file string

All 4 of these methods render their <script> tags into the HTML body. The script blocks give you a little bit of control by having a ‘top’ and ‘bottom’ of the document location which gives you some flexibility over script placement and precedence. Script includes and resource url unfortunately do not even get that much control – references are simply rendered into the page in the order of declaration.

The ASP.NET ScriptManager control facilitates this task a little bit with the abililty to specify scripts in code and the ability to programmatically check what scripts have already been registered, but it doesn’t provide any more control over the script rendering process itself. Further the ScriptManager is a bear to deal with generically because generic code has to always check and see if it is actually present.

Some time ago I posted a ClientScriptProxy class that helps with managing the latter process of sending script references either to ClientScript or ScriptManager if it’s available. Since I last posted about this there have been a number of improvements in this API, one of which is the ability to control placement of scripts and script includes in the page which I think is rather important and a missing feature in the ASP.NET native functionality.

Handling ScriptRenderModes

One of the big enhancements that I’ve come to rely on is the ability of the various script rendering functions described above to support rendering in multiple locations:

/// <summary>
/// Determines how scripts are included into the page
/// </summary>
public enum ScriptRenderModes
{
    /// <summary>
    /// Inherits the setting from the control or from the ClientScript.DefaultScriptRenderMode
    /// </summary>
    Inherit,
    /// Renders the script include at the location of the control
    /// </summary>
    Inline,
    /// <summary>
    /// Renders the script include into the bottom of the header of the page
    /// </summary>
    Header,
    /// <summary>
    /// Renders the script include into the top of the header of the page
    /// </summary>
    HeaderTop,
    /// <summary>
    /// Uses ClientScript or ScriptManager to embed the script include to
    /// provide standard ASP.NET style rendering in the HTML body.
    /// </summary>
    Script,
    /// <summary>
    /// Renders script at the bottom of the page before the last Page.Controls
    /// literal control. Note this may result in unexpected behavior 
    /// if /body and /html are not the last thing in the markup page.
    /// </summary>
    BottomOfPage        
}

This enum is then applied to the various Register functions to allow more control over where scripts actually show up. Why is this useful? For me I often render scripts out of control resources and these scripts often include things like a JavaScript Library (jquery) and a few plug-ins. The order in which these can be loaded is critical so that jQuery.js always loads before any plug-in for example.

Typically I end up with a general script layout like this:

  • Core Libraries- HeaderTop
  • Plug-ins: Header
  • ScriptBlocks: Header or Script depending on other dependencies

There’s also an option to render scripts and CSS at the very bottom of the page before the last Page control on the page which can be useful for speeding up page load when lots of scripts are loaded.

The API syntax of the ClientScriptProxy methods is closely compatible with ScriptManager’s using static methods and control references to gain access to the page and embedding scripts.

For example, to render some script into the current page in the header:

// Create script block in header
ClientScriptProxy.Current.RegisterClientScriptBlock(this, typeof(ControlResources),
                "hello_function", "function helloWorld() { alert('hello'); }", true,
                ScriptRenderModes.Header);

// Same again - shouldn't be rendered because it's the same id
ClientScriptProxy.Current.RegisterClientScriptBlock(this, typeof(ControlResources),
         "hello_function", "function helloWorld() { alert('hello'); }", true,
         ScriptRenderModes.Header);

// Create a second script block in header
ClientScriptProxy.Current.RegisterClientScriptBlock(this, typeof(ControlResources),
    "hello_function2", "function helloWorld2() { alert('hello2'); }", true,
    ScriptRenderModes.Header);


// This just calls ClientScript and renders into bottom of document
ClientScriptProxy.Current.RegisterStartupScript(this,typeof(ControlResources),
                "call_hello", "helloWorld();helloWorld2();", true);

which generates:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
</title>
<script type="text/javascript">
function helloWorld() { alert('hello'); }
</script>

<script type="text/javascript">
function helloWorld2() { alert('hello2'); }
</script>
</head>
<body>
…    
<script type="text/javascript">
//<![CDATA[
helloWorld();helloWorld2();//]]>
</script>
</form>
</body>
</html>

Note that the scripts are generated into the header rather than the body except for the last script block which is the call to RegisterStartupScript. In general I wouldn’t recommend using RegisterStartupScript – ever. It’s a much better practice to use a script base load event to handle ‘startup’ code that should fire when the page first loads. So instead of the code above I’d actually recommend doing:

ClientScriptProxy.Current.RegisterClientScriptBlock(this, typeof(ControlResources),
    "call_hello", "$().ready( function() { alert('hello2'); });", true,
    ScriptRenderModes.Header);

assuming you’re using jQuery on the page.

For script includes from a Url the following demonstrates how to embed scripts into the header. This example injects a jQuery and jQuery.UI script reference from the Google CDN then checks each with a script block to ensure that it has loaded and if not loads it from a server local location:

// load jquery from CDN
ClientScriptProxy.Current.RegisterClientScriptInclude(this, typeof(ControlResources),
                "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js",
                ScriptRenderModes.HeaderTop);

// check if jquery loaded - if it didn't we're not online
string scriptCheck =
    @"if (typeof jQuery != 'object')  
        document.write(unescape(""%3Cscript src='{0}' type='text/javascript'%3E%3C/script%3E""));";

string jQueryUrl = ClientScriptProxy.Current.GetWebResourceUrl(this, typeof(ControlResources),
                ControlResources.JQUERY_SCRIPT_RESOURCE);            
ClientScriptProxy.Current.RegisterClientScriptBlock(this, typeof(ControlResources),
                "jquery_register", string.Format(scriptCheck,jQueryUrl),true,
                ScriptRenderModes.HeaderTop);                            

// Load jquery-ui from cdn
ClientScriptProxy.Current.RegisterClientScriptInclude(this, typeof(ControlResources),
                "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js",
                ScriptRenderModes.Header);

// check if we need to load from local
string jQueryUiUrl = ResolveUrl("~/scripts/jquery-ui-custom.min.js"); 
ClientScriptProxy.Current.RegisterClientScriptBlock(this, typeof(ControlResources),
    "jqueryui_register", string.Format(scriptCheck, jQueryUiUrl), true,
    ScriptRenderModes.Header); 


// Create script block in header
ClientScriptProxy.Current.RegisterClientScriptBlock(this, typeof(ControlResources),
                "hello_function", "$().ready( function() { alert('hello'); });", true,
                ScriptRenderModes.Header);

which in turn generates this HTML:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
    if (typeof jQuery != 'object')
        document.write(unescape("%3Cscript src='/WestWindWebToolkitWeb/WebResource.axd?d=DIykvYhJ_oXCr-TA_dr35i4AayJoV1mgnQAQGPaZsoPM2LCdvoD3cIsRRitHKlKJfV5K_jQvylK7tsqO3lQIFw2&t=633979863959332352' type='text/javascript'%3E%3C/script%3E"));
</script>
<title>
</title>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" type="text/javascript"></script>
<script type="text/javascript">
    if (typeof jQuery != 'object')
        document.write(unescape("%3Cscript src='/WestWindWebToolkitWeb/scripts/jquery-ui-custom.min.js' type='text/javascript'%3E%3C/script%3E"));
</script>

<script type="text/javascript">
    $().ready(function() { alert('hello'); });
</script>
</head>
<body>
…
</body> </html>

As you can see there’s a bit more control in this process as you can inject both script includes and script blocks into the document at the top or bottom of the header, plus if necessary at the usual body locations. This is quite useful especially if you create custom server controls that interoperate with script and have certain dependencies. The above is a good example of a useful switchable routine where you can switch where scripts load from by default – the above pulls from Google CDN but a configuration switch may automatically switch to pull from the local development copies if your doing development for example.

How does it work?

As mentioned the ClientScriptProxy object mimicks many of the ScriptManager script related methods and so provides close API compatibility with it although it contains many additional overloads that enhance functionality. It does however work against ScriptManager if it’s available on the page, or Page.ClientScript if it’s not so it provides a single unified frontend to script access. There are however many overloads of the original SM methods like the above to provide additional functionality.

The implementation of script header rendering is pretty straight forward – as long as a server header (ie. it has to have runat=”server” set) is available. Otherwise these routines fall back to using the default document level insertions of ScriptManager/ClientScript. Given that there is a server header it’s relatively easy to generate the script tags and code and append them to the header either at the top or bottom. I suspect Microsoft didn’t provide header rendering functionality precisely because a runat=”server” header is not required by ASP.NET so behavior would be slightly unpredictable. That’s not really a problem for a custom implementation however.

Here’s the RegisterClientScriptBlock implementation that takes a ScriptRenderModes parameter to allow header rendering:

/// <summary>
/// Renders client script block with the option of rendering the script block in
/// the Html header
/// 
/// For this to work Header must be defined as runat="server"
/// </summary>
/// <param name="control">any control that instance typically page</param>
/// <param name="type">Type that identifies this rendering</param>
/// <param name="key">unique script block id</param>
/// <param name="script">The script code to render</param>
/// <param name="addScriptTags">Ignored for header rendering used for all other insertions</param>
/// <param name="renderMode">Where the block is rendered</param>
public void RegisterClientScriptBlock(Control control, Type type, string key, string script, bool addScriptTags, ScriptRenderModes renderMode)
{
    if (renderMode == ScriptRenderModes.Inherit)
        renderMode = DefaultScriptRenderMode;

    if (control.Page.Header == null || 
        renderMode != ScriptRenderModes.HeaderTop && 
        renderMode != ScriptRenderModes.Header &&
        renderMode != ScriptRenderModes.BottomOfPage)
    {
        RegisterClientScriptBlock(control, type, key, script, addScriptTags);
        return;
    }

    // No dupes - ref script include only once
    const string identifier = "scriptblock_";
    if (HttpContext.Current.Items.Contains(identifier + key))
        return;
    HttpContext.Current.Items.Add(identifier + key, string.Empty);

    StringBuilder sb = new StringBuilder();

    // Embed in header
    sb.AppendLine("\r\n<script type=\"text/javascript\">");
    sb.AppendLine(script);            
    sb.AppendLine("</script>");

    int? index = HttpContext.Current.Items["__ScriptResourceIndex"] as int?;
    if (index == null)
        index = 0;

    if (renderMode == ScriptRenderModes.HeaderTop)
    {
        control.Page.Header.Controls.AddAt(index.Value, new LiteralControl(sb.ToString()));
        index++;
    }
    else if(renderMode == ScriptRenderModes.Header)
        control.Page.Header.Controls.Add(new LiteralControl(sb.ToString()));
    else if (renderMode == ScriptRenderModes.BottomOfPage)
        control.Page.Controls.AddAt(control.Page.Controls.Count-1,new LiteralControl(sb.ToString()));

    
    HttpContext.Current.Items["__ScriptResourceIndex"] = index;            
}

Note that the routine has to keep track of items inserted by id so that if the same item is added again with the same key it won’t generate two script entries. Additionally the code has to keep track of how many insertions have been made at the top of the document so that entries are added in the proper order.

The RegisterScriptInclude method is similar but there’s some additional logic in here to deal with script file references and ClientScriptProxy’s (optional) custom resource handler that provides script compression

/// <summary>
/// Registers a client script reference into the page with the option to specify
/// the script location in the page
/// </summary>
/// <param name="control">Any control instance - typically page</param>
/// <param name="type">Type that acts as qualifier (uniqueness)</param>
/// <param name="url">the Url to the script resource</param>
/// <param name="ScriptRenderModes">Determines where the script is rendered</param>
public void RegisterClientScriptInclude(Control control, Type type, string url, ScriptRenderModes renderMode)
{
    const string STR_ScriptResourceIndex = "__ScriptResourceIndex";

    if (string.IsNullOrEmpty(url))
        return;

    if (renderMode == ScriptRenderModes.Inherit)
        renderMode = DefaultScriptRenderMode;

    // Extract just the script filename
    string fileId = null;
    
    
    // Check resource IDs and try to match to mapped file resources
    // Used to allow scripts not to be loaded more than once whether
    // embedded manually (script tag) or via resources with ClientScriptProxy
    if (url.Contains(".axd?r="))
    {
        string res = HttpUtility.UrlDecode( StringUtils.ExtractString(url, "?r=", "&", false, true) );
        foreach (ScriptResourceAlias item in ScriptResourceAliases)
        {
            if (item.Resource == res)
            {
                fileId = item.Alias + ".js";
                break;
            }
        }
        if (fileId == null)
            fileId = url.ToLower();
    }
    else
        fileId = Path.GetFileName(url).ToLower();

    // No dupes - ref script include only once
    const string identifier = "script_";
    if (HttpContext.Current.Items.Contains( identifier + fileId ) )
        return;
    
    HttpContext.Current.Items.Add(identifier + fileId, string.Empty);

    // just use script manager or ClientScriptManager
    if (control.Page.Header == null || renderMode == ScriptRenderModes.Script || renderMode == ScriptRenderModes.Inline)
    {
        RegisterClientScriptInclude(control, type,url, url);
        return;
    }

    // Retrieve script index in header            
    int? index = HttpContext.Current.Items[STR_ScriptResourceIndex] as int?;
    if (index == null)
        index = 0;

    StringBuilder sb = new StringBuilder(256);

    url = WebUtils.ResolveUrl(url);

    // Embed in header
    sb.AppendLine("\r\n<script src=\"" + url + "\" type=\"text/javascript\"></script>");

    if (renderMode == ScriptRenderModes.HeaderTop)
    {
        control.Page.Header.Controls.AddAt(index.Value, new LiteralControl(sb.ToString()));
        index++;
    }
    else if (renderMode == ScriptRenderModes.Header)
        control.Page.Header.Controls.Add(new LiteralControl(sb.ToString()));
    else if (renderMode == ScriptRenderModes.BottomOfPage)
        control.Page.Controls.AddAt(control.Page.Controls.Count-1, new LiteralControl(sb.ToString()));                

    HttpContext.Current.Items[STR_ScriptResourceIndex] = index;
}

There’s a little more code here that deals with cleaning up the passed in Url and also some custom handling of script resources that run through the ScriptCompressionModule – any script resources loaded in this fashion are automatically cached based on the resource id. Raw urls extract just the filename from the URL and cache based on that. All of this to avoid doubling up of scripts if called multiple times by multiple instances of the same control for example or several controls that all load the same resources/includes.

Finally RegisterClientScriptResource utilizes the previous method to wrap the WebResourceUrl as well as some custom functionality for the resource compression module:

/// <summary>
/// Returns a WebResource or ScriptResource URL for script resources that are to be
/// embedded as script includes.
/// </summary>
/// <param name="control">Any control</param>
/// <param name="type">A type in assembly where resources are located</param>
/// <param name="resourceName">Name of the resource to load</param>
/// <param name="renderMode">Determines where in the document the link is rendered</param>
public void RegisterClientScriptResource(Control control, Type type, 
                                         string resourceName, 
                                         ScriptRenderModes renderMode)
{ 
    string resourceUrl = GetClientScriptResourceUrl(control, type, resourceName);
    RegisterClientScriptInclude(control, type, resourceUrl, renderMode);
}
/// <summary>
/// Works like GetWebResourceUrl but can be used with javascript resources
/// to allow using of resource compression (if the module is loaded).
/// </summary>
/// <param name="control"></param>
/// <param name="type"></param>
/// <param name="resourceName"></param>
/// <returns></returns>
public string GetClientScriptResourceUrl(Control control, Type type, string resourceName)
{            
    #if IncludeScriptCompressionModuleSupport

    // If wwScriptCompression Module through Web.config is loaded use it to compress 
    // script resources by using wcSC.axd Url the module intercepts
    if (ScriptCompressionModule.ScriptCompressionModuleActive) 
    {
        string url = "~/wwSC.axd?r=" + HttpUtility.UrlEncode(resourceName);
        if (type.Assembly != GetType().Assembly)
            url += "&t=" + HttpUtility.UrlEncode(type.FullName);
        
        return WebUtils.ResolveUrl(url);
    }
    
    #endif

    return control.Page.ClientScript.GetWebResourceUrl(type, resourceName);
}

This code merely retrieves the resource URL and then simply calls back to RegisterClientScriptInclude with the URL to be embedded which means there’s nothing specific to deal with other than the custom compression module logic which is nice and easy.

What else is there in ClientScriptProxy?

ClientscriptProxy also provides a few other useful services beyond what I’ve already covered here:

Transparent ScriptManager and ClientScript calls

ClientScriptProxy includes a host of routines that help figure out whether a script manager is available or not and all functions in this class call the appropriate object – ScriptManager or ClientScript – that is available in the current page to ensure that scripts get embedded into pages properly. This is especially useful for control development where controls have no control over the scripting environment in place on the page.

RegisterCssLink and RegisterCssResource
Much like the script embedding functions these two methods allow embedding of CSS links. CSS links are appended to the header or to a form declared with runat=”server”.

LoadControlScript

Is a high level resource loading routine that can be used to easily switch between different script linking modes. It supports loading from a WebResource, a url or not loading anything at all. This is very useful if you build controls that deal with specification of resource urls/ids in a standard way.

Check out the full Code

You can check out the full code to the ClientScriptProxyClass here:

ClientScriptProxy.cs

ClientScriptProxy Documentation (class reference)

Note that the ClientScriptProxy has a few dependencies in the West Wind Web Toolkit of which it is part of. ControlResources holds a few standard constants and script resource links and the ScriptCompressionModule which is referenced in a few of the script inclusion methods.

There’s also another useful ScriptContainer companion control  to the ClientScriptProxy that allows scripts to be placed onto the page’s markup including the ability to specify the script location and script minification options.

You can find all the dependencies in the West Wind Web Toolkit repository:

West Wind Web Toolkit Repository

West Wind Web Toolkit Home Page



LINQ to SQL and missing Many to Many EntityRefs



Ran into an odd behavior today with a many to many mapping of one of my tables in LINQ to SQL. Many to many mappings aren’t transparent in LINQ to SQL and it maps the link table the same way the SQL schema has it when creating one. In other words LINQ to SQL isn’t smart about many to many mappings and just treats it like the 3 underlying tables that make up the many to many relationship. Iain Galloway has a nice blog entry about Many to Many relationships in LINQ to SQL.

I can live with that – it’s not really difficult to deal with this arrangement once mapped, especially when reading data back. Writing is a little more difficult as you do have to insert into two entities for new records, but nothing that can’t be handled in a small business object method with a few lines of code.

When I created a database I’ve been using to experiment around with various different OR/Ms recently I found that for some reason LINQ to SQL was completely failing to map even to the linking table. As it turns out there’s a good reason why it fails, can you spot it below? (read on :-})

Here is the original database layout:

Schema

There’s an items table, a category table and a link table that holds only the foreign keys to the Items and Category tables for a typical M->M relationship.

When these three tables are imported into the model the *look* correct – I do get the relationships added (after modifying the entity names to strip the prefix):

Model

The relationship looks perfectly fine, both in the designer as well as in the XML document:

  <Table Name="dbo.wws_Item_Categories" Member="ItemCategories">
    <Type Name="ItemCategory">
      <Column Name="ItemId" Type="System.Guid" DbType="uniqueidentifier NOT NULL" CanBeNull="false" />
      <Column Name="CategoryId" Type="System.Guid" DbType="uniqueidentifier NOT NULL" CanBeNull="false" />
      <Association Name="ItemCategory_Category" Member="Categories" ThisKey="CategoryId" OtherKey="Id" Type="Category" />
      <Association Name="Item_ItemCategory" Member="Item" ThisKey="ItemId" OtherKey="Id" Type="Item" IsForeignKey="true" />
    </Type>
  </Table>
  <Table Name="dbo.wws_Categories" Member="Categories">
    <Type Name="Category">
      <Column Name="Id" Type="System.Guid" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
      <Column Name="ParentId" Type="System.Guid" DbType="UniqueIdentifier" CanBeNull="true" />
      <Column Name="CategoryName" Type="System.String" DbType="NVarChar(150)" CanBeNull="true" />
      <Column Name="CategoryDescription" Type="System.String" DbType="NVarChar(MAX)" CanBeNull="true" />
      <Column Name="tstamp" AccessModifier="Internal" Type="System.Data.Linq.Binary" DbType="rowversion" CanBeNull="true" IsVersion="true" />
      <Association Name="ItemCategory_Category" Member="ItemCategory" ThisKey="Id" OtherKey="CategoryId" Type="ItemCategory" IsForeignKey="true" />
    </Type>
  </Table>

However when looking at the code generated these navigation properties (also on Item) are completely missing:

[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.wws_Item_Categories")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class ItemCategory : Westwind.BusinessFramework.EntityBase
{
    private System.Guid _ItemId;
    private System.Guid _CategoryId;
    
    public ItemCategory()
    {
    }
    
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ItemId", DbType="uniqueidentifier NOT NULL")]
    [global::System.Runtime.Serialization.DataMemberAttribute(Order=1)]
    public System.Guid ItemId
    {
        get
        {
            return this._ItemId;
        }
        set
        {
            if ((this._ItemId != value))
            {
                this._ItemId = value;
            }
        }
    }
    
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_CategoryId", DbType="uniqueidentifier NOT NULL")]
    [global::System.Runtime.Serialization.DataMemberAttribute(Order=2)]
    public System.Guid CategoryId
    {
        get
        {
            return this._CategoryId;
        }
        set
        {
            if ((this._CategoryId != value))
            {
                this._CategoryId = value;
            }
        }
    }
}

Notice that the Item and Category association properties which should be EntityRef properties are completely missing. They’re there in the model, but the generated code – not so much.

So what’s the problem here?

The problem – it appears – is that LINQ to SQL requires primary keys on all entities it tracks. In order to support tracking – even of the link table entity – the link table requires a primary key. Real obvious ain’t it, especially since the designer happily lets you import the table and even shows the relationship and implicitly the related properties.

Adding an Id field as a Pk to the database and then importing results in this model layout:

ModelLinkWithPk

which properly generates the Item and Category properties into the link entity.

It’s ironic that LINQ to SQL *requires* the PK in the middle – the Entity Framework requires that a link table have *only* the two foreign key fields in a table in order to recognize a many to many relation. EF actually handles the M->M relation directly without the intermediate link entity unlike LINQ to SQL.

[updated from comments – 12/24/2009]

Another approach is to set up both ItemId and CategoryId in the database which shows up in LINQ to SQL like this:

CompoundPrimary Key

This also work in creating the Category and Item fields in the ItemCategory entity. Ultimately this is probably the best approach as it also guarantees uniqueness of the keys and so helps in database integrity.

It took me a while to figure out WTF was going on here – lulled by the designer to think that the properties should be when they were not. It’s actually a well documented feature of L2S that each entity in the model requires a Pk but of course that’s easy to miss when the model viewer shows it to you and even the underlying XML model shows the Associations properly.

This is one of the issue with L2S of course – you have to play by its rules and once you hit one of those rules there’s no way around them – you’re stuck with what it requires which in this case meant changing the database.



Making Sense of ASP.NET Paths



ASP.Net includes quite a plethora of properties to retrieve path information about the current request, control and application. There's a ton of information available about paths on the Request object, some of it appearing to overlap and some of it buried several levels down, and it can be confusing to find just the right path that you are looking for.

To keep things straight I thought it a good idea to summarize the path options along with descriptions and example paths. I wrote a post about this a long time ago in 2004 and I find myself frequently going back to that page to quickly figure out which path I’m looking for in processing the current URL. Apparently a lot of people must be doing the same, because the original post is the second most visited even to this date on this blog to the tune of nearly 500 hits per day. So, I decided to update and expand a bit on the original post with a little more information and clarification based on the original comments.

Request Object Paths Available

Here's a list of the Path related properties on the Request object (and the Page object). Assume a path like http://www.west-wind.com/webstore/admin/paths.aspx for the paths below where webstore is the name of the virtual.

Request Property Description and Value
ApplicationPath Returns the web root-relative logical path to the virtual root of this app.
/webstore/
PhysicalApplicationPath Returns local file system path of the virtual root for this app.
c:\inetpub\wwwroot\webstore
PhysicalPath Returns the local file system path to the current script or path.
c:\inetpub\wwwroot\webstore\admin\paths.aspx
Path
FilePath
CurrentExecutionFilePath
All of these return the full root relative logical path to the script page including path and scriptname. CurrentExcecutionFilePath will return the ‘current’ request path after a Transfer/Execute call while FilePath will always return the original request’s path.
/webstore/admin/paths.aspx
AppRelativeCurrentExecutionFilePath Returns an ASP.NET root relative virtual path to the script or path for the current request. If in  a Transfer/Execute call the transferred Path is returned.
~/admin/paths.aspx
PathInfo Returns any extra path following the script name. If no extra path is provided returns the root-relative path (returns text in red below). string.Empty if no PathInfo is available.
/webstore/admin/paths.aspx/ExtraPathInfo
RawUrl Returns the full root relative URL including querystring and extra path as a string.
/webstore/admin/paths.aspx?sku=wwhelp40
Url Returns a fully qualified URL including querystring and extra path. Note this is a Uri instance rather than string.
http://www.west-wind.com/webstore/admin/paths.aspx?sku=wwhelp40
UrlReferrer The fully qualified URL of the page that sent the request. This is also a Uri instance and this value is null if the page was directly accessed by typing into the address bar or using an HttpClient based Referrer client Http header.
http://www.west-wind.com/webstore/default.aspx?Info
Control.TemplateSourceDirectory Returns the logical path to the folder of the page, master or user control on which it is called. This is useful if you need to know the path only to a Page or control from within the control. For non-file controls this returns the Page path.
/webstore/admin/

As you can see there’s a ton of information available there for each of the three common path formats:

  • Physical Path
    is an OS type path that points to a path or file on disk.
  • Logical Path
    is a Web path that is relative to the Web server’s root. It includes the virtual plus the application relative path.
  • ~/ (Root-relative) Path
    is an ASP.NET specific path that includes ~/ to indicate the virtual root Web path. ASP.NET can convert virtual paths into either logical paths using Control.ResolveUrl(), or physical paths using Server.MapPath(). Root relative paths are useful for specifying portable URLs that don’t rely on relative directory structures and very useful from within control or component code.

You should be able to get any necessary format from ASP.NET from just about any path or script using these mechanisms.

~/ Root Relative Paths and ResolveUrl() and ResolveClientUrl()

ASP.NET supports root-relative virtual path syntax in most of its URL properties in Web Forms. So you can easily specify a root relative path in a control rather than a location relative path:

<asp:Image runat="server" ID="imgHelp"  ImageUrl="~/images/help.gif" />

ASP.NET internally resolves this URL by using ResolveUrl("~/images/help.gif") to arrive at the root-relative URL of /webstore/images/help.gif which uses the Request.ApplicationPath as the basepath to replace the ~. By convention any custom Web controls also should use ResolveUrl() on URL properties to provide the same functionality.

In your own code you can use Page.ResolveUrl() or Control.ResolveUrl() to accomplish the same thing:

string imgPath = this.ResolveUrl("~/images/help.gif");
imgHelp.ImageUrl = imgPath;

Unfortunately ResolveUrl() is limited to WebForm pages, so if you’re in an HttpHandler or Module it’s not available.

ASP.NET Mvc also has it’s own more generic version of ResolveUrl in Url.Decode:

<script src="<%= Url.Content("~/scripts/new.js") %>" type="text/javascript"></script> 

which is part of the UrlHelper class. In ASP.NET MVC the above sort of syntax is actually even more crucial than in WebForms due to the fact that views are not referencing specific pages but rather are often path based which can lead to various variations on how a particular view is referenced.

In a Module or Handler code Control.ResolveUrl() unfortunately is not available which in retrospect seems like an odd design choice – URL resolution really should happen on a Request basis not as part of the Page framework. Luckily you can also rely on the static VirtualPathUtility class:

string path = VirtualPathUtility.ToAbsolute("~/admin/paths.aspx");

VirtualPathUtility also many other quite useful methods for dealing with paths and converting between the various kinds of paths supported. One thing to watch out for is that ToAbsolute() will throw an exception if a query string is provided and doesn’t work on fully qualified URLs. I wrote about this topic with a custom solution that works fully qualified URLs and query strings here (check comments for some interesting discussions too).

Similar to ResolveUrl() is ResolveClientUrl() which creates a fully qualified HTTP path that includes the protocol and domain name. It’s rare that this full resolution is needed but can be useful in some scenarios.

Mapping Virtual Paths to Physical Paths with Server.MapPath()

If you need to map root relative or current folder relative URLs to physical URLs or you can use HttpContext.Current.Server.MapPath(). Inside of a Page you can do the following:

string physicalPath = Server.MapPath("~/scripts/ww.jquery.js"));

MapPath is pretty flexible and it understands both ASP.NET style virtual paths as well as plain relative paths, so the following also works.

string physicalPath = Server.MapPath("scripts/silverlight.js");

as well as dot relative syntax:

string physicalPath = Server.MapPath("../scripts/jquery.js");

Once you have the physical path you can perform standard System.IO Path and File operations on the file. Remember with physical paths and IO or copy operations you need to make sure you have permissions to access files and folders based on the Web server user account that is active (NETWORK SERVICE, ASPNET typically).

Note the Server.MapPath will not map up beyond the virtual root of the application for security reasons.

Server and Host Information

Between these settings you can get all the information you may need to figure out where you are at and to build new Url if necessary. If you need to build a URL completely from scratch you can get access to information about the server you are accessing:

Server Variable Function and Example
SERVER_NAME The of the domain or IP Address
wwww.west-wind.com or 127.0.0.1
SERVER_PORT The port that the request runs under.
80
SERVER_PORT_SECURE Determines whether https: was used.
0 or 1
APPL_MD_PATH ADSI DirectoryServices path to the virtual root directory. Note that LM typically doesn’t work for ADSI access so you should replace that with LOCALHOST or the machine’s NetBios name.
/LM/W3SVC/1/ROOT/webstore

Request.Url and Uri Parsing

If you still need more control over the current request URL or  you need to create new URLs from an existing one, the current Request.Url Uri property offers a lot of control. Using the Uri class and UriBuilder makes it easy to retrieve parts of a URL and create new URLs based on existing URL. The UriBuilder class is the preferred way to create URLs – much preferable over creating URIs via string concatenation.

Uri Property Function
Scheme The URL scheme or protocol prefix.
http or https
Port The port if specifically specified.
DnsSafeHost The domain name or local host NetBios machine name
www.west-wind.com or rasnote
LocalPath The full path of the URL including script name and extra PathInfo.
/webstore/admin/paths.aspx
Query The query string if any
?id=1

The Uri class itself is great for retrieving Uri parts, but most of the properties are read only if you need to modify a URL in order to change it you can use the UriBuilder class to load up an existing URL and modify it to create a new one.

Here are a few common operations I’ve needed to do to get specific URLs:

Convert the Request URL to an SSL/HTTPS link

For example to take the current request URL and converted  it to a secure URL can be done like this:

UriBuilder build = new UriBuilder(Request.Url);
build.Scheme = "https";
build.Port = -1;  // don't inject port

Uri newUri = build.Uri;
string newUrl = build.ToString();

Retrieve the fully qualified URL without a QueryString
AFAIK, there’s no native routine to retrieve the current request URL without the query string. It’s easy to do with UriBuilder however:

UriBuilder builder = newUriBuilder(Request.Url);
builder.Query = "";
stringlogicalPathWithoutQuery = builder.ToString();

What else?

I took a look through the old post’s comments and addressed as many of the questions and comments that came up in there. With a few small and silly exceptions this update post handles most of these.

But I’m sure there are a more things that go in here. What else would be useful to put onto this post so it serves as a nice all in one place to go for path references? If you think of something leave a comment and I’ll try to update the post with it in the future.



SmtpClient and Locked File Attachments



Got a note a couple of days ago from a client using one of my generic routines that wraps SmtpClient. Apparently whenever a file has been attached to a message and emailed with SmtpClient the file remains locked after the message has been sent. Oddly this particular issue hasn’t cropped up before for me although these routines are in use in a number of applications I’ve built.

The wrapper I use was built mainly to backfit an old pre-.NET 2.0 email client I built using Sockets to avoid the CDO nightmares of the .NET 1.x mail client. The current class retained the same class interface but now internally uses SmtpClient which holds a flat property interface that makes it less verbose to send off email messages.

File attachments in this interface are handled by providing a comma delimited list for files in an Attachments string property which is then collected along with the other flat property settings and eventually passed on to SmtpClient in the form of a MailMessage structure.

The jist of the code is something like this:

/// <summary>
/// Fully self contained mail sending method. Sends an email message by connecting 
/// and disconnecting from the email server.
/// </summary>
/// <returns>true or false</returns>
public bool SendMail()
{
    if (!this.Connect())
        return false;

    try
    {
        // Create and configure the message 
        MailMessage msg = this.GetMessage();

        smtp.Send(msg);

        this.OnSendComplete(this);

    }
    catch (Exception ex)
    {
        string msg = ex.Message;
        if (ex.InnerException != null)                
            msg = ex.InnerException.Message;
        
        this.SetError(msg);
        this.OnSendError(this);

        return false;
    }
    finally
    {
        // close connection and clear out headers
// SmtpClient instance nulled out
this.Close(); } return true; }
/// <summary>
/// Configures the message interface
/// </summary>
/// <param name="msg"></param>
protected virtual MailMessage GetMessage()
{
    MailMessage msg = new MailMessage();            

    msg.Body = this.Message;
    msg.Subject = this.Subject;
    msg.From = new MailAddress(this.SenderEmail, this.SenderName);

    if (!string.IsNullOrEmpty(this.ReplyTo))
        msg.ReplyTo = new MailAddress(this.ReplyTo);

    // Send all the different recipients
    this.AssignMailAddresses(msg.To, this.Recipient);
    this.AssignMailAddresses(msg.CC, this.CC);
    this.AssignMailAddresses(msg.Bcc, this.BCC);

    if (!string.IsNullOrEmpty(this.Attachments))
    {
        string[] files = this.Attachments.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string file in files)
        {
            msg.Attachments.Add(new Attachment(file));
        }
    }

    if (this.ContentType.StartsWith("text/html"))
        msg.IsBodyHtml = true;
    else
        msg.IsBodyHtml = false;

    msg.BodyEncoding = this.Encoding;
    … additional code  omitted

    return msg;
}

Basically this code collects all the property settings of the wrapper object and applies them to the SmtpClient and in GetMessage() to an individual MailMessage properties. Specifically notice that attachment filenames are converted from a comma-delimited string to filenames from which new attachments are created.

The code as it’s written however, will cause the problem with file attachments not being released properly. Internally .NET opens up stream handles and reads the files from disk to dump them into the email send stream. The attachments are always sent correctly but the local files are not immediately closed.

As you probably guessed the issue is simply that some resources are not automatcially disposed when sending is complete and sure enough the following code change fixes the problem:

// Create and configure the message 
using (MailMessage msg = this.GetMessage())
{
    smtp.Send(msg);

    if (this.SendComplete != null)
        this.OnSendComplete(this);
    // or use an explicit msg.Dispose() here
}

The Message object requires an explicit call to Dispose() (or a using() block as I have here) to force the attachment files to get closed.

I think this is rather odd behavior for this scenario however. The code I use passes in filenames and my expectation of an API that accepts file names is that it uses the files by opening and streaming them and then closing them when done. Why keep the streams open and require an explicit .Dispose() by the calling code which is bound to lead to unexpected behavior just as my customer ran into? Any API level code should clean up as much as possible and this is clearly not happening here resulting in unexpected behavior. Apparently lots of other folks have run into this before as I found based on a few Twitter comments on this topic.

Odd to me too is that SmtpClient() doesn’t implement IDisposable – it’s only the MailMessage (and Attachments) that implement it and require it to clean up for left over resources like open file handles. This means that you couldn’t even use a using() statement around the SmtpClient code to resolve this – instead you’d have to wrap it around the message object which again is rather unexpected.

Well, chalk that one up to another small unexpected behavior that wasted a half an hour of my time – hopefully this post will help someone avoid this same half an hour of hunting and searching.

Resources:



A free standing ASP.NET Pager Web Control



Paging in ASP.NET has been relatively easy with stock controls supporting basic paging functionality. However, recently I built an MVC application and one of the things I ran into was that I HAD TO build manual paging support into a few of my pages. Dealing with list controls and rendering markup is easy enough, but doing paging is a little more involved. I ended up with a small but flexible component that can be dropped anywhere. As it turns out the task of creating a semi-generic Pager control for MVC was fairly easily.

Now I’m back to working in Web Forms and thought to myself that the way I created the pager in MVC actually would also work in ASP.NET – in fact quite a bit easier since the whole thing can be conveniently wrapped up into an easily reusable control. A standalone pager would provider easier reuse in various pages and a more consistent pager display regardless of what kind of 'control’ the pager is associated with.

Why a Pager Control?

At first blush it might sound silly to create a new pager control – after all Web Forms has pretty decent paging support, doesn’t it? Well, sort of. Yes the GridView control has automatic paging built in and the ListView control has the related DataPager control.

The built in ASP.NET paging has several issues though:

  • Postback and JavaScript requirements
    If you look at paging links in ASP.NET they are always postback links with javascript:__doPostback() calls that go back to the server. While that works fine and actually has some benefit like the fact that paging saves changes to the page and post them back, it’s not very SEO friendly. Basically if you use javascript based navigation nosearch engine will follow the paging links which effectively cuts off list content on the first page. The DataPager control does support GET based links via the QueryStringParameter property, but the control is effectively tied to the ListView control (which is the only control that implements IPageableItemContainer).
  • DataSource Controls required for Efficient Data Paging Retrieval
    The only way you can get paging to work efficiently where only the few records you display on the page are queried for and retrieved from the database you have to use a DataSource control - only the Linq and Entity DataSource controls  support this natively. While you can retrieve this data yourself manually, there’s no way to just assign the page number and render the pager based on this custom subset. Other than that default paging requires a full resultset for ASP.NET to filter the data and display only a subset which can be very resource intensive and wasteful if you’re dealing with largish resultsets (although I’m a firm believer in returning actually usable sets :-}). If you use your own business layer that doesn’t fit an ObjectDataSource you’re SOL. That’s a real shame too because with LINQ based querying it’s real easy to retrieve a subset of data that is just the data you want to display but the native Pager functionality doesn’t support just setting properties to display just the subset AFAIK.
  • DataPager is not Free Standing
    The DataPager control is the closest thing to a decent Pager implementation that ASP.NET has, but alas it’s not a free standing component – it works off a related control and the only one that it effectively supports from the stock ASP.NET controls is the ListView control. This means you can’t use the same data pager formatting for a grid and a list view or vice versa and you’re always tied to the control.
  • Paging Events
    In order to handle paging you have to deal with paging events. The events fire at specific time instances in the page pipeline and because of this you often have to handle data binding in a way to work around the paging events or else end up double binding your data sources based on paging. Yuk.
  • Styling
    The GridView pager is a royal pain to beat into submission for styled rendering. The DataPager control has many more options and template layout and it renders somewhat cleaner, but it too is not exactly easy to get a decent display for.
  • Not a Generic Solution
    The problem with the ASP.NET controls too is that it’s not generic. GridView, DataGrid use their own internal paging, ListView can use a DataPager and if you want to manually create data layout – well you’re on your own. IOW, depending on what you use you likely have very different looking Paging experiences.

So, I figured I’ve struggled with this once too many and finally sat down and built a Pager control.

The Pager Control

My goal was to create a totally free standing control that has no dependencies on other controls and certainly no requirements for using DataSource controls. The idea is that you should be able to use this pager control without any sort of data requirements at all – you should just be able to set properties and be able to display a pager.

The Pager control I ended up with has the following features:

  • Completely free standing Pager control – no control or data dependencies
  • Complete manual control – Pager can render without any data dependency
  • Easy to use: Only need to set PageSize, ActivePage and TotalItems
  • Supports optional filtering of IQueryable for efficient queries and Pager rendering
  • Supports optional full set filtering of IEnumerable<T> and DataTable
  • Page links are plain HTTP GET href Links
  • Control automatically picks up Page links on the URL and assigns them
    (automatic page detection no page index changing events to hookup)
  • Full CSS Styling support

On the downside there’s no templating support for the control so the layout of the pager is relatively fixed. All elements however are stylable and there are options to control the text, and layout options such as whether to display first and last pages and the previous/next buttons and so on.

To give you an idea what the pager looks like, here are two differently styled examples (all via CSS):

Pagers[4] 

The markup for these two pagers looks like this:

<ww:Pager runat="server" id="ItemPager" 
          PageSize="5" 
          PageLinkCssClass="gridpagerbutton" 
          SelectedPageCssClass="gridpagerbutton-selected"
          PagesTextCssClass="gridpagertext"
          CssClass="gridpager" 
          RenderContainerDiv="true"
          ContainerDivCssClass="gridpagercontainer"
          MaxPagesToDisplay="6" 
          PagesText="Item Pages:" 
          NextText="next"
          PreviousText="previous"
          />
<ww:Pager runat="server" id="ItemPager2" 
          PageSize="5" 
          RenderContainerDiv="true"
          MaxPagesToDisplay="6" />

The latter example uses default style settings so it there’s not much to set. The first example on the other hand explicitly assigns custom styles and overrides a few of the formatting options.

Styling

The styling is based on a number of CSS classes of which the the main pager, pagerbutton and pagerbutton-selected classes are the important ones. Other styles like pagerbutton-next/prev/first/last are based on the pagerbutton style.

The default styling shown for the red outlined pager looks like this:

.pagercontainer
{
    margin: 20px 0;
    background: whitesmoke;    
    padding: 5px;    
}
.pager
{
    float: right;
    font-size: 10pt;
    text-align: left;
}
.pagerbutton,.pagerbutton-selected,.pagertext
{
    display: block;        
    float: left;    
    text-align: center;
    border: solid 2px maroon;        
    min-width: 18px;      
    margin-left: 3px;    
    text-decoration: none;        
    padding: 4px;
}
.pagerbutton-selected
{
    font-size: 130%;
    font-weight: bold;        
    color: maroon;
    border-width: 0px;
    background: khaki;
}
.pagerbutton-first
{
    margin-right: 12px;        
}
.pagerbutton-last,.pagerbutton-prev
{
    margin-left: 12px;        
}
.pagertext
{
    border: none;
    margin-left: 30px;
    font-weight: bold;
}
.pagerbutton a
{
    text-decoration: none;
}
.pagerbutton:hover
{
    background-color: maroon;
    color: cornsilk;
}
.pagerbutton-prev
{
    background-image: url(images/prev.png);
    background-position: 2px center;
    background-repeat: no-repeat;
    width: 35px;
    padding-left: 20px;
}
.pagerbutton-next
{
    background-image: url(images/next.png);
    background-position: 40px center;
    background-repeat: no-repeat;
    width: 35px;
    padding-right: 20px;
    margin-right: 0px;
}

Yup that’s a lot of styling settings although not all of them are required. The key ones are pagerbutton, pager and pager selection. The others (which are implicitly created by the control based on the pagerbutton style) are for custom markup of the ‘special’ buttons.

In my apps I tend to have two kinds of pages: Those that are associated with typical ‘grid’ displays that display purely tabular data and those that have a more looser list like layout. The two pagers shown above represent these two views and the pager and gridpager styles in my standard style sheet reflect these two styles.

Configuring the Pager with Code

Finally lets look at what it takes to hook up the pager. As mentioned in the highlights the Pager control is completely independent of other controls so if you just want to display a pager on its own it’s as simple as dropping the control and assigning the PageSize, ActivePage and either TotalPages or TotalItems.

So for this markup:

<ww:Pager runat="server" id="ItemPagerManual" 
          PageSize="5" 
          MaxPagesToDisplay="6" 
/>

I can use code as simple as:

ItemPagerManual.PageSize = 3;
ItemPagerManual.ActivePage = 4;
ItemPagerManual.TotalItems = 20;

Note that ActivePage is not required - it will automatically use any Page=x query string value and assign it, although you can override it as I did above. TotalItems can be any value that you retrieve from a result set or manually assign as I did above.

A more realistic scenario based on a LINQ to SQL IQueryable result is even easier. In this example, I have a UserControl that contains a ListView control that renders IQueryable data. I use a User Control here because there are different views the user can choose from with each view being a different user control. This incidentally also highlights one of the nice features of the pager: Because the pager is independent of the control I can put the pager on the host page instead of into each of the user controls. IOW, there’s only one Pager control, but there are potentially many user controls/listviews that hold the actual display data.

The following code demonstrates how to use the Pager with an IQueryable that loads only the records it displays: protected voidPage_Load(objectsender, EventArgs e)
{
    Category = Request.Params["Category"] ?? string.Empty;

    IQueryable<wws_Item> ItemList = ItemRepository.GetItemsByCategory(Category);

   
// Update the page and filter the list down
    ItemList = ItemPager.FilterIQueryable<wws_Item>(ItemList);

    // Render user control with a list view     


Control ulItemList = LoadControl("~/usercontrols/" + App.Configuration.ItemListType + ".ascx"); ((IInventoryItemListControl)ulItemList).InventoryItemList = ItemList; phItemList.Controls.Add(ulItemList); // placeholder }

The code uses a business object to retrieve Items by category as an IQueryable which means that the result is only an expression tree that hasn’t execute SQL yet and can be further filtered. I then pass this IQueryable to the FilterIQueryable() helper method of the control which does two main things:

  • Filters the IQueryable to retrieve only the data displayed on the active page
  • Sets the Totaltems property and calculates TotalPages on the Pager

and that’s it! When the Pager renders it uses those values, plus the PageSize and ActivePage properties to render the Pager.

In addition to IQueryable there are also filter methods for IEnumerable<T> and DataTable, but these versions just filter the data by removing rows/items from the entire already retrieved data.

Output Generated and Paging Links

The output generated creates pager links as plain href links. Here’s what the output looks like:

<div id="ItemPager" class="pagercontainer">
    <div class="pager">
        <span class="pagertext">Pages: </span><a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=1" class="pagerbutton" />1</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=2" class="pagerbutton" />2</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=3" class="pagerbutton" />3</a>
        <span class="pagerbutton-selected">4</span>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=5" class="pagerbutton" />5</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=6" class="pagerbutton" />6</a>
        <a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=20" class="pagerbutton pagerbutton-last" />20</a>&nbsp;<a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=3" class="pagerbutton pagerbutton-prev" />Prev</a>&nbsp;<a href="http://localhost/WestWindWebStore/itemlist.aspx?Page=5" class="pagerbutton pagerbutton-next" />Next</a></div>
        <br clear="all" />
    </div>
</div>

The links point back to the current page and simply append a Page= page link into the page. When the page gets reloaded with the new page number the pager automatically detects the page number and automatically assigns the ActivePage property which results in the appropriate page to be displayed. The code shown in the previous section is all that’s needed to handle paging.

Note that HTTP GET based paging is different than the Postback paging ASP.NET uses by default. Postback paging preserves modified page content when clicking on pager buttons, but this control will simply load a new page – no page preservation at this time.

The advantage of not using Postback paging is that the URLs generated are plain HTML links that a search engine can follow where __doPostback() links are not.

Pager with a Grid

The pager also works in combination with grid controls so it’s easy to bypass the grid control’s paging features if desired. In the following example I use a gridView control and binds it to a DataTable result which is also filterable by the Pager control.

The very basic plain vanilla ASP.NET grid markup looks like this:

   <div style="width: 600px; margin: 0 auto;padding: 20px; ">
        <asp:DataGrid runat="server" AutoGenerateColumns="True" 
                      ID="gdItems" CssClass="blackborder" style="width: 600px;">
        <AlternatingItemStyle CssClass="gridalternate" /> 
        <HeaderStyle CssClass="gridheader" />
        </asp:DataGrid>
        
        <ww:Pager runat="server" ID="Pager" 
              CssClass="gridpager"
              ContainerDivCssClass="gridpagercontainer"
              PageLinkCssClass="gridpagerbutton"
              SelectedPageCssClass="gridpagerbutton-selected"
              PageSize="8" 
              RenderContainerDiv="true"
              MaxPagesToDisplay="6"  />    
    </div>

and looks like this when rendered:
GridPager
using custom set of CSS styles. The code behind for this code is also very simple:

protected void Page_Load(object sender, EventArgs e)
{
    string category = Request.Params["category"] ?? "";

    busItem itemRep = WebStoreFactory.GetItem();
    var items = itemRep.GetItemsByCategory(category)
                       .Select(itm => new {Sku = itm.Sku, Description = itm.Description});           

    // run query into a DataTable for demonstration
    DataTable dt = itemRep.Converter.ToDataTable(items,"TItems");

    // Remove all items not on the current page
    dt = Pager.FilterDataTable(dt,0);
    
    // bind and display
    gdItems.DataSource = dt;
    gdItems.DataBind();
}

A little contrived I suppose since the list could already be bound from the list of elements, but this is to demonstrate that you can also bind against a DataTable if your business layer returns those.

Unfortunately there’s no way to filter a DataReader as it’s a one way forward only reader and the reader is required by the DataSource to perform the bindings.  However, you can still use a DataReader as long as your business logic filters the data prior to rendering and provides a total item count (most likely as a second query).

Control Creation

The control itself is a pretty brute force ASP.NET control. Nothing clever about this other than some basic rendering logic and some simple calculations and update routines to determine which buttons need to be shown. You can take a look at the full code from the West Wind Web Toolkit’s Repository (note there are a few dependencies).

To give you an idea how the control works here is the Render() method:

/// <summary>
/// overridden to handle custom pager rendering for runtime and design time
/// </summary>
/// <param name="writer"></param>
protected override void Render(HtmlTextWriter writer)
{
    base.Render(writer);

    if (TotalPages == 0 && TotalItems > 0)                            
       TotalPages = CalculateTotalPagesFromTotalItems();  

    if (DesignMode)
        TotalPages = 10;

    // don't render pager if there's only one page
    if (TotalPages < 2)
        return;

    if (RenderContainerDiv)
    {
        if (!string.IsNullOrEmpty(ContainerDivCssClass))
            writer.AddAttribute("class", ContainerDivCssClass);
        writer.RenderBeginTag("div");
    }

    // main pager wrapper
    writer.WriteBeginTag("div");
    writer.AddAttribute("id", this.ClientID);
    if (!string.IsNullOrEmpty(CssClass))
        writer.WriteAttribute("class", this.CssClass);
    writer.Write(HtmlTextWriter.TagRightChar + "\r\n");


    // Pages Text
    writer.WriteBeginTag("span");
    if (!string.IsNullOrEmpty(PagesTextCssClass))
        writer.WriteAttribute("class", PagesTextCssClass);
    writer.Write(HtmlTextWriter.TagRightChar);
    writer.Write(this.PagesText);
    writer.WriteEndTag("span");

    // if the base url is empty use the current URL
    FixupBaseUrl();        

    // set _startPage and _endPage
    ConfigurePagesToRender();

    // write out first page link
    if (ShowFirstAndLastPageLinks && _startPage != 1)
    {
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, (1).ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-first");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write("1");
        writer.WriteEndTag("a");
        writer.Write("&nbsp;");
    }

    // write out all the page links
    for (int i = _startPage; i < _endPage + 1; i++)
    {
        if (i == ActivePage)
        {
            writer.WriteBeginTag("span");
            if (!string.IsNullOrEmpty(SelectedPageCssClass))
                writer.WriteAttribute("class", SelectedPageCssClass);
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.Write(i.ToString());
            writer.WriteEndTag("span");
        }
        else
        {
            writer.WriteBeginTag("a");
            string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, i.ToString()).TrimEnd('&');
            writer.WriteAttribute("href", pageUrl);
            if (!string.IsNullOrEmpty(PageLinkCssClass))
                writer.WriteAttribute("class", PageLinkCssClass);
            writer.Write(HtmlTextWriter.SelfClosingTagEnd);
            writer.Write(i.ToString());
            writer.WriteEndTag("a");
        }

        writer.Write("\r\n");
    }

    // write out last page link
    if (ShowFirstAndLastPageLinks && _endPage < TotalPages)
    {
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, TotalPages.ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-last");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write(TotalPages.ToString());
        writer.WriteEndTag("a");
    }


    // Previous link
    if (ShowPreviousNextLinks && !string.IsNullOrEmpty(PreviousText) && ActivePage > 1)
    {
        writer.Write("&nbsp;");
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, (ActivePage - 1).ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-prev");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write(PreviousText);
        writer.WriteEndTag("a");
    }

    // Next link
    if (ShowPreviousNextLinks && !string.IsNullOrEmpty(NextText) && ActivePage < TotalPages)
    {
        writer.Write("&nbsp;");
        writer.WriteBeginTag("a");
        string pageUrl = StringUtils.SetUrlEncodedKey(BaseUrl, QueryStringPageField, (ActivePage + 1).ToString());
        writer.WriteAttribute("href", pageUrl);
        if (!string.IsNullOrEmpty(PageLinkCssClass))
            writer.WriteAttribute("class", PageLinkCssClass + " " + PageLinkCssClass + "-next");
        writer.Write(HtmlTextWriter.SelfClosingTagEnd);
        writer.Write(NextText);
        writer.WriteEndTag("a");
    }

    writer.WriteEndTag("div");

    if (RenderContainerDiv)
    {
        if (RenderContainerDivBreak)
writer.Write("<br clear=\"all\" />\r\n"); writer.WriteEndTag("div"); } }

As I said pretty much brute force rendering based on the control’s property settings of which there are quite a few:

PagerVisualStudio

You can also see the pager in the designer above. unfortunately the VS designer (both 2010 and 2008) fails to render the float: left CSS styles properly and starts wrapping after margins are applied in the special buttons. Not a big deal since VS does at least respect the spacing (the floated elements overlay). Then again I’m not using the designer anyway :-}.

Filtering Data

What makes the Pager easy to use is the filter methods built into the control. While this functionality is clearly not the most politically correct design choice as it violates separation of concerns, it’s very useful for typical pager operation. While I actually have filter methods that do something similar in my business layer, having it exposed on the control makes the control a lot more useful for typical databinding scenarios. Of course these methods are optional – if you have a business layer that can provide filtered page queries for you can use that instead and assign the TotalItems property manually.

There are three filter method types available for IQueryable, IEnumerable and for DataTable which tend to be the most common use cases in my apps old and new. The IQueryable version is pretty simple as it can simply rely on on .Skip() and .Take() with LINQ:

 /// <summary>
 /// <summary>
 /// Queries the database for the ActivePage applied manually
 /// or from the Request["page"] variable. This routine
 /// figures out and sets TotalPages, ActivePage and
 /// returns a filtered subset IQueryable that contains
 /// only the items from the ActivePage.
 /// </summary>
 /// <param name="query"></param>
 /// <param name="activePage">
 /// The page you want to display. Sets the ActivePage property when passed. 
 /// Pass 0 or smaller to use ActivePage setting.
 /// </param>
 /// <returns></returns>
 public IQueryable<T> FilterIQueryable<T>(IQueryable<T> query, int activePage)
       where T : class, new()
 {
     ActivePage = activePage < 1 ? ActivePage : activePage;
     if (ActivePage < 1)
         ActivePage = 1;

     TotalItems = query.Count(); 

     if (TotalItems <= PageSize)
     {
         ActivePage = 1;
         TotalPages = 1;
         return query;
     }

     int skip = ActivePage - 1;
     if (skip > 0)
         query = query.Skip(skip * PageSize);

     _TotalPages = CalculateTotalPagesFromTotalItems();

     return query.Take(PageSize);
 }


The IEnumerable<T> version simply  converts the IEnumerable to an IQuerable and calls back into this method for filtering.

The DataTable version requires a little more work to manually parse and filter records (I didn’t want to add the Linq DataSetExtensions assembly just for this):

/// <summary>
/// Filters a data table for an ActivePage.
/// 
/// Note: Modifies the data set permanently by remove DataRows
/// </summary>
/// <param name="dt">Full result DataTable</param>
/// <param name="activePage">Page to display. 0 to use ActivePage property </param>
/// <returns></returns>
public DataTable FilterDataTable(DataTable dt, int activePage)
{
    ActivePage = activePage < 1 ? ActivePage : activePage;
    if (ActivePage < 1)
        ActivePage = 1;

    TotalItems = dt.Rows.Count;

    if (TotalItems <= PageSize)
    {
        ActivePage = 1;
        TotalPages = 1;
        return dt;
    }
    
    int skip = ActivePage - 1;            
    if (skip > 0)
    {
        for (int i = 0; i < skip * PageSize; i++ )
            dt.Rows.RemoveAt(0);
    }
    while(dt.Rows.Count > PageSize)
        dt.Rows.RemoveAt(PageSize);

    return dt;
}

Using the Pager Control

The pager as it is is a first cut I built a couple of weeks ago and since then have been tweaking a little as part of an internal project I’m working on. I’ve replaced a bunch of pagers on various older pages with this pager without any issues and have what now feels like a more consistent user interface where paging looks and feels the same across different controls. As a bonus I’m only loading the data from the database that I need to display a single page. With the preset class tags applied too adding a pager is now as easy as dropping the control and adding the style sheet for styling to be consistent – no fuss, no muss. Schweet.

Hopefully some of you may find this as useful as I have or at least as a baseline to build ontop of…

Resources



Visual Studio 2010 Beta 2 Startup Failures



I’ve been working with VS 2010 Beta 2 for a while now and while it works Ok most of the time it seems the environment is very, very fragile when it comes to crashes and installed packages. Specifically I’ve been working just fine for days, then when VS 2010 crashes it will not re-start. Instead I get the good old Application cannot start dialog:

ApplicationCannotStart

Other failures I’ve seen bring forth other just as useful dialogs with information overload like Operation cannot be performed which for me specifically happens when trying to compile any project.

After a bit of digging around and a post to Microsoft Connect the solution boils down to resetting the VS.NET environment. The Application Cannot Start issue stems from a package load failure of some sort, so the work around for this is typically:

c:\program files\Visual Studio 2010\Common7\IDE\devenv.exe /ResetSkipPkgs

In most cases that should do the trick. If it doesn’t and the error doesn’t go away the more drastic:

c:\program files\Visual Studio 2010\Common7\IDE\devenv.exe /ResetSettings

is required which resets all settings in VS to its installation defaults. Between these two I’ve always been able to get VS to startup and run properly.

BTW it’s handy to keep a list of command line options for Visual Studio around:

http://msdn.microsoft.com/en-us/library/xee0c8y7%28VS.100%29.aspx

Note that the /? option in VS 2010 doesn’t display all the options available but rather displays the ‘demo version’ message instead, so the above should be helpful. Also note that unless you install Visual C++ the Visual Studio Command Prompt icon is not automatically installed so you may have to navigate manually to the appropriate folder above.

Cannot Build Failures

If you get the Cannot compile error dialog, there is another thing that have worked for me: Change your project build target from Debug to Release (or whatever – just change it) and compile again. If that doesn’t work doing the reset steps above will do it for me.

It appears this failure comes from some sort of interference of other versions of Visual Studio installed on the system and running another version first. Resetting the build target explicitly seems to reset the build providers to a normalized state so that things work in many cases. But not all. Worst case – resetting settings will do it.

The bottom line for working in VS 2010 has been – don’t get too attached to your custom settings as they will get blown away quite a bit. I’ve probably been through 20 or more of these VS resets although I’ve been working with it quite a bit on an internal project.

It’s kind of frustrating to see this kind of high level instability in a Beta 2 product which is supposedly the last public beta they will put out. On the other hand this beta has been otherwise rather stable and performance is roughly equivalent to VS 2008. Although I mention the crash above – crashes I’ve seen have been relatively rare and no more frequent than in VS 2008 it seems. Given the drastic UI changes in VS 2010 (using WPF for the shell and editor) I’m actually impressed that the product is as stable as it is at this point. Also I was seriously worried about text quality going to a WPF model, but thankfully WPF 4.0 addresses the blurry text issue with native font rendering to render text on non-cleartype enabled systems crisply.

Anyway I hope that these notes are helpful to some of you playing around with the beta and running into problems. Hopefully you won’t need them :-}



DevConnections Slides and Samples Posted



I’ve posted the slides and samples to my DevConnections Sessions for anyone interested. I had a lot of fun with my sessions this time around mainly because the sessions picked were a little off the beaten track (well, the handlers/modules and e-commerce sessions anyway). For those of you that attended I hope you found the sessions useful. For the rest of you – you can check out the slides and samples if you like. Here’s what was covered:

Introduction to jQuery with ASP.NET

This session covered mostly the client side of jQuery demonstrated on a small sample page with a variety of incrementally built up examples of selection and page manipulation. This session also introduces some of the basics of AJAX communication talking to ASP.NET. When I do this session it never turns out exactly the same way and this time around the examples were on the more basic side and purely done with hands on demonstrations rather than walk throughs of more complex examples. Alas this session always feels like it needs another half an hour to get through the full sortiment of functionality. The slides and samples cover a wider variety of topics and there are many examples that demonstrate more advanced operations like interacting with WCF REST services, using client templating and building rich client only windowed interfaces.

Download

Low Level ASP.NET: Handlers and Modules

This session was a look at the ASP.NET pipeline and it discusses some of the ASP.NET base architecture and key components from HttpRuntime on up through the various modules and handlers that make up the ASP.NET/IIS pipeline. This session is fun as there are a number of cool examples that demonstrate the power and flexibility of ASP.NET, but some of the examples were external and interfacing with other technologies so they’re not actually included in the downloadable samples. However, there are still a few cool ones in there – there’s an image resizing handler, an image overlay module that stamps images with Sample if loaded from a certain folder, an OpenID authentication module (which failed during the demo due to the crappy internet connection at DevConnections this year :-}), Response filtering using a generic filter stream component, a generic error handler and a few others. The slides cover a lot of the ASP.NET pipeline flow and various HttpRuntime components.

Download

Electronic Payment Processing in ASP.NET Applications

This session covered the business end and integration of electronic credit card processing and PayPal. A good part of this session deals with what’s involved in payment processing, getting signed up and who you have to deal with for your merchant account. We then took a look at integration of credit card processing via some generic components provided with the session that allow processing using a unified class interface with specific implementations for several of the most common gateway providers including Authorize.NET, PayFlowPro, LinkPoint, BluePay etc. We also briefly looked at PayPal Classic implementation which provides a quick and cheap if not quite as professional mechanism for taking payments online. The samples provide the Credit Card processing wrappers for the various gateway providers as well as a PayPal helper class to generate the PayPal redirect urls as well as helper code for dealing with IPN callbacks.

Download

Hope some of you will find the material useful. Enjoy.



Capturing and Transforming ASP.NET Output with Response.Filter



During one of my Handlers and Modules session at DevConnections this week one of the attendees asked a question that I didn’t have an immediate answer for. Basically he wanted to capture response output completely and then apply some filtering to the output – effectively injecting some additional content into the page AFTER the page had completely rendered. Specifically the output should be captured from anywhere – not just a page and have this code injected into the page.

Some time ago I posted some code that allows you to capture ASP.NET Page output by overriding the Render() method, capturing the HtmlTextWriter() and reading its content, modifying the rendered data as text then writing it back out. I’ve actually used this approach on a few occasions and it works fine for ASP.NET pages. But this obviously won’t work outside of the Page class environment and it’s not really generic – you have to create a custom page class in order to handle the output capture.

[updated 11/16/2009 – updated ResponseFilterStream implementation and a few additional notes based on comments]

Enter Response.Filter

However, ASP.NET includes a Response.Filter which can be used – well to filter output. Basically Response.Filter is a stream through which the OutputStream is piped back to the Web Server (indirectly). As content is written into the Response object, the filter stream receives the appropriate Stream commands like Write, Flush and Close as well as read operations although for a Response.Filter that’s uncommon to be hit. The Response.Filter can be programmatically replaced at runtime which allows you to effectively intercept all output generation that runs through ASP.NET.

A common Example: Dynamic GZip Encoding

A rather common use of Response.Filter hooking up code based, dynamic  GZip compression for requests which is dead simple by applying a GZipStream (or DeflateStream) to Response.Filter. The following generic routines can be used very easily to detect GZip capability of the client and compress response output with a single line of code and a couple of library helper routines:

WebUtils.GZipEncodePage();
which is handled with a few lines of reusable code and a couple of static helper methods:

/// <summary>
///
Sets up the current page or handler to use GZip through a Response.Filter
///
IMPORTANT: 
///
You have to call this method before any output is generated!
/// </summary>
public static void GZipEncodePage()
{
    HttpResponse Response = HttpContext.Current.Response;

    if(IsGZipSupported())
    {
        stringAcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
        if(AcceptEncoding.Contains("deflate"))
        {
            Response.Filter = newSystem.IO.Compression.DeflateStream(Response.Filter,
                                       System.IO.Compression.CompressionMode.Compress);
            Response.AppendHeader("Content-Encoding", "deflate");
        }
       
else
      
{
            Response.Filter = newSystem.IO.Compression.GZipStream(Response.Filter,
                                      System.IO.Compression.CompressionMode.Compress);
            Response.AppendHeader("Content-Encoding", "gzip");                   
        }
    }

   
// Allow proxy servers to cache encoded and unencoded versions separately
  
Response.AppendHeader("Vary", "Content-Encoding");
}
/// <summary>
/// Determines if GZip is supported
/// </summary>
/// <returns></returns>
public static bool IsGZipSupported()
{
    string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
    if (!string.IsNullOrEmpty(AcceptEncoding) &&
         (AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))
        return true;
    return false;
}

GZipStream and DeflateStream are streams that are assigned to Response.Filter and by doing so apply the appropriate compression on the active Response.

Response.Filter content is chunked

So to implement a Response.Filter effectively requires only that you implement a custom stream and handle the Write() method to capture Response output as it’s written. At first blush this seems very simple – you capture the output in Write, transform it and write out the transformed content in one pass. And that indeed works for small amounts of content. But you see, the problem is that output is written in small buffer chunks (a little less than 16k it appears) rather than just a single Write() statement into the stream, which makes perfect sense for ASP.NET to stream data back to IIS in smaller chunks to minimize memory usage en route.

Unfortunately this also makes it a more difficult to implement any filtering routines since you don’t directly get access to all of the response content which is problematic especially if those filtering routines require you to look at the ENTIRE response in order to transform or capture the output as is needed for the solution the gentleman in my session asked for.

So in order to address this a slightly different approach is required that basically captures all the Write() buffers passed into a cached stream and then making the stream available only when it’s complete and ready to be flushed.

As I was thinking about the implementation I also started thinking about the few instances when I’ve used Response.Filter implementations. Each time I had to create a new Stream subclass and create my custom functionality but in the end each implementation did the same thing – capturing output and transforming it. I thought there should be an easier way to do this by creating a re-usable Stream class that can handle stream transformations that are common to Response.Filter implementations.

Creating a semi-generic Response Filter Stream Class

What I ended up with is a ResponseFilterStream class that provides a handful of Events that allow you to capture and/or transform Response content. The class implements a subclass of Stream and then overrides Write() and Flush() to handle capturing and transformation operations. By exposing events it’s easy to hook up capture or transformation operations via single focused methods.

ResponseFilterStream exposes the following events:

  • CaptureStream, CaptureString
    Captures the output only and provides either a MemoryStream or String with the final page output. Capture is hooked to the Flush() operation of the stream.
  • TransformStream, TransformString
    Allows you to transform the complete response output with events that receive a MemoryStream or String respectively and can you modify the output then return it back as a return value. The transformed output is then written back out in a single chunk to the response output stream. These events capture all output internally first then write the entire buffer into the response.
  • TransformWrite, TransformWriteString
    Allows you to transform the Response data as it is written in its original chunk size in the Stream’s Write() method. Unlike TransformStream/TransformString which operate on the complete output, these events only see the current chunk of data written. This is more efficient as there’s no caching involved, but can cause problems due to searched content splitting over multiple chunks.

Using this implementation, creating a custom Response.Filter transformation becomes as simple as the following code.

To hook up the Response.Filter using the MemoryStream version event:

ResponseFilterStream filter = new ResponseFilterStream(Response.Filter);
filter.TransformStream += filter_TransformStream;
Response.Filter = filter;  

and the event handler to do the transformation:

MemoryStream filter_TransformStream(MemoryStream ms)
{
    Encoding encoding = HttpContext.Current.Response.ContentEncoding;
    
    string output = encoding.GetString(ms.ToArray());

    output = FixPaths(output);
    
    ms = new MemoryStream(output.Length);

    byte[] buffer = encoding.GetBytes(output);
    ms.Write(buffer,0,buffer.Length);

    return ms;
}
private string FixPaths(string output)
{
    string path = HttpContext.Current.Request.ApplicationPath;


// override root path wonkiness if (path == "/") path = ""; output = output.Replace("\"~/", "\"" + path + "/").Replace("'~/", "'" + path + "/"); return output; }

The idea of the event handler is that you can do whatever you want to the stream and return back a stream – either the same one that’s been modified or a brand new one – which is then sent back to as the final response.

The above code can be simplified even more by using the string version events which handle the stream to string conversions for you:

ResponseFilterStream filter = new ResponseFilterStream(Response.Filter);
filter.TransformString += filter_TransformString;
Response.Filter = filter;                

and the event handler to do the transformation calling the same FixPaths method shown above:

string filter_TransformString(string output)
{
    return FixPaths(output);
}

The events for capturing output and capturing and transforming chunks work in a very similar way. By using events to handle the transformations ResponseFilterStream becomes a reusable component and we don’t have to create a new stream class or subclass an existing Stream based classed.

By the way, the example used here is kind of a cool trick which transforms “~/” expressions inside of the final generated HTML output – even in plain HTML controls not HTML controls – and transforms them into the appropriate application relative path in the same way that ResolveUrl would do.

So you can write plain old HTML like this:

<a href=”~/default.aspx”>Home</a> 

and have it turned into:

<a href=”/myVirtual/default.aspx”>Home</a> 

without having to use an ASP.NET control like Hyperlink or Image or having to constantly use:

<img src=”<%= ResolveUrl(“~/images/home.gif”) %>” />

in MVC applications (which frankly is one of the most annoying things about MVC especially given the path hell that extension-less and endpoint-less URLs impose).

I can’t take credit for this idea. While discussing the Response.Filter issues on Twitter a hint from Dylan Beattie who pointed me at one of his examples which does something similar. I thought the idea was cool enough to use an example for future demos of Response.Filter functionality in ASP.NET next I time I do the Modules and Handlers talk (which was great fun BTW).

How practical this is is debatable however since there’s definitely some overhead to using a Response.Filter in general and especially on one that caches the output and the re-writes it later. Make sure to test for performance anytime you use Response.Filter hookup and make sure it' doesn’t end up killing perf on you. You’ve been warned :-}.

How does ResponseFilterStream work?

The big win of this implementation IMHO is that it’s a reusable  component – so for implementation there’s no new class, no subclassing – you simply attach to an event to implement an event handler method with a straight forward signature to retrieve the stream or string you’re interested in.

The implementation is based on a subclass of Stream as is required in order to handle the Response.Filter requirements. What’s different than other implementations I’ve seen in various places is that it supports capturing output as a whole to allow retrieving the full response output for capture or modification. The exception are the TransformWrite and TransformWrite events which operate only active chunk of data written by the Response.

For captured output, the Write() method captures output into an internal MemoryStream that is cached until writing is complete. So Write() is called when ASP.NET writes to the Response stream, but the filter doesn’t pass on the Write immediately to the filter’s internal stream. The data is cached and only when the Flush() method is called to finalize the Stream’s output do we actually send the cached stream off for transformation (if the events are hooked up) and THEN finally write out the returned content in one big chunk.

Here’s the implementation of ResponseFilterStream:

/// <summary>
/// A semi-generic Stream implementation for Response.Filter with
/// an event interface for handling Content transformations via
/// Stream or String.    
/// <remarks>
/// Use with care for large output as this implementation copies
/// the output into a memory stream and so increases memory usage.
/// </remarks>
/// </summary>    
public class ResponseFilterStream : Stream
{
    /// <summary>
    /// The original stream
    /// </summary>
    Stream _stream;

    /// <summary>
    /// Current position in the original stream
    /// </summary>
    long _position;

    /// <summary>
    /// Stream that original content is read into
    /// and then passed to TransformStream function
    /// </summary>
    MemoryStream _cacheStream = new MemoryStream(5000);

    /// <summary>
    /// Internal pointer that that keeps track of the size
    /// of the cacheStream
    /// </summary>
    int _cachePointer = 0;


    /// <summary>
    /// 
    /// </summary>
    /// <param name="responseStream"></param>
    public ResponseFilterStream(Stream responseStream)
    {
        _stream = responseStream;
    }


    /// <summary>
    /// Determines whether the stream is captured
    /// </summary>
    private bool IsCaptured
    {
        get 
        {

            if (CaptureStream != null || CaptureString != null ||
                TransformStream != null || TransformString != null)
                return true;

            return false;
        }
    }

    /// <summary>
    /// Determines whether the Write method is outputting data immediately
    /// or delaying output until Flush() is fired.
    /// </summary>
    private bool IsOutputDelayed
    {
        get 
        {
            if (TransformStream != null || TransformString != null)
                return true;

            return false;
        }        
    }
    

    /// <summary>
    /// Event that captures Response output and makes it available
    /// as a MemoryStream instance. Output is captured but won't 
    /// affect Response output.
    /// </summary>
    public event Action<MemoryStream> CaptureStream;

    /// <summary>
    /// Event that captures Response output and makes it available
    /// as a string. Output is captured but won't affect Response output.
    /// </summary>
    public event Action<string> CaptureString;

    

    /// <summary>
    /// Event that allows you transform the stream as each chunk of
    /// the output is written in the Write() operation of the stream.
    /// This means that that it's possible/likely that the input 
    /// buffer will not contain the full response output but only
    /// one of potentially many chunks.
    /// 
    /// This event is called as part of the filter stream's Write() 
    /// operation.
    /// </summary>
    public event Func<byte[], byte[]> TransformWrite;


    /// <summary>
    /// Event that allows you to transform the response stream as
    /// each chunk of bytep[] output is written during the stream's write
    /// operation. This means it's possibly/likely that the string
    /// passed to the handler only contains a portion of the full
    /// output. Typical buffer chunks are around 16k a piece.
    /// 
    /// This event is called as part of the stream's Write operation.
    /// </summary>
    public event Func<string, string> TransformWriteString;

    /// <summary>
    /// This event allows capturing and transformation of the entire 
    /// output stream by caching all write operations and delaying final
    /// response output until Flush() is called on the stream.
    /// </summary>
    public event Func<MemoryStream, MemoryStream> TransformStream;

    /// <summary>
    /// Event that can be hooked up to handle Response.Filter
    /// Transformation. Passed a string that you can modify and
    /// return back as a return value. The modified content
    /// will become the final output.
    /// </summary>
    public event Func<string, string> TransformString;


    protected virtual void OnCaptureStream(MemoryStream ms)
    {
        if (CaptureStream != null)
            CaptureStream(ms);
    }


    private void OnCaptureStringInternal(MemoryStream ms)
    {
        if (CaptureString != null)
        {
            string content = HttpContext.Current.Response.ContentEncoding.GetString(ms.ToArray());
            OnCaptureString(content);
        }
    }

    protected virtual void OnCaptureString(string output)
    {
        if (CaptureString != null)
            CaptureString(output);
    }

    protected virtual byte[] OnTransformWrite(byte[] buffer)
    {
        if (TransformWrite != null)
            return TransformWrite(buffer);
        return buffer;
    }

    private byte[] OnTransformWriteStringInternal(byte[] buffer)
    {
        Encoding encoding = HttpContext.Current.Response.ContentEncoding;
        string output = OnTransformWriteString(encoding.GetString(buffer));
        return encoding.GetBytes(output);
    }

    private string OnTransformWriteString(string value)
    {
        if (TransformWriteString != null)
            return TransformWriteString(value);
        return value;
    }


    protected virtual MemoryStream OnTransformCompleteStream(MemoryStream ms)
    {
        if (TransformStream != null)
            return TransformStream(ms);

        return ms;
    }




    /// <summary>
    /// Allows transforming of strings
    /// 
    /// Note this handler is internal and not meant to be overridden
    /// as the TransformString Event has to be hooked up in order
    /// for this handler to even fire to avoid the overhead of string
    /// conversion on every pass through.
    /// </summary>
    /// <param name="responseText"></param>
    /// <returns></returns>
    private string OnTransformCompleteString(string responseText)
    {
        if (TransformString != null)
            TransformString(responseText);

        return responseText;
    }

    /// <summary>
    /// Wrapper method form OnTransformString that handles
    /// stream to string and vice versa conversions
    /// </summary>
    /// <param name="ms"></param>
    /// <returns></returns>
    internal MemoryStream OnTransformCompleteStringInternal(MemoryStream ms)
    {
        if (TransformString == null)
            return ms;

        //string content = ms.GetAsString();
        string content = HttpContext.Current.Response.ContentEncoding.GetString(ms.ToArray());

        content = TransformString(content);
        byte[] buffer = HttpContext.Current.Response.ContentEncoding.GetBytes(content);
        ms = new MemoryStream();
        ms.Write(buffer, 0, buffer.Length);
        //ms.WriteString(content);

        return ms;
    }

    /// <summary>
    /// 
    /// </summary>
    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return true; }
    }
    /// <summary>
    /// 
    /// </summary>
    public override bool CanWrite
    {
        get { return true; }
    }

    /// <summary>
    /// 
    /// </summary>
    public override long Length
    {
        get { return 0; }
    }

    /// <summary>
    /// 
    /// </summary>
    public override long Position
    {
        get { return _position; }
        set { _position = value; }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="offset"></param>
    /// <param name="direction"></param>
    /// <returns></returns>
    public override long Seek(long offset, System.IO.SeekOrigin direction)
    {
        return _stream.Seek(offset, direction);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="length"></param>
    public override void SetLength(long length)
    {
        _stream.SetLength(length);
    }

    /// <summary>
    /// 
    /// </summary>
    public override void Close()
    {
        _stream.Close();
    }

    /// <summary>
    /// Override flush by writing out the cached stream data
    /// </summary>
    public override void Flush()
    {

        if (IsCaptured && _cacheStream.Length > 0)
        {
            // Check for transform implementations
            _cacheStream = OnTransformCompleteStream(_cacheStream);
            _cacheStream = OnTransformCompleteStringInternal(_cacheStream);
            
            OnCaptureStream(_cacheStream);
            OnCaptureStringInternal(_cacheStream);

            // write the stream back out if output was delayed
            if (IsOutputDelayed)
                _stream.Write(_cacheStream.ToArray(), 0, (int)_cacheStream.Length);

            // Clear the cache once we've written it out
            _cacheStream.SetLength(0);
        }

        // default flush behavior
        _stream.Flush();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public override int Read(byte[] buffer, int offset, int count)
    {
        return _stream.Read(buffer, offset, count);
    }


    /// <summary>
    /// Overriden to capture output written by ASP.NET and captured
    /// into a cached stream that is written out later when Flush()
    /// is called.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    public override void Write(byte[] buffer, int offset, int count)
    {
        if ( IsCaptured )
        {
            // copy to holding buffer only - we'll write out later
            _cacheStream.Write(buffer, 0, count);
            _cachePointer += count;
        }

        // just transform this buffer
        if (TransformWrite != null)
            buffer = OnTransformWrite(buffer);
        if (TransformWriteString != null)
            buffer = OnTransformWriteStringInternal(buffer);

        if (!IsOutputDelayed)
            _stream.Write(buffer, offset, buffer.Length);
        
    }

}

The key features are the events and corresponding OnXXX methods that handle the event hookups, and the Write() and Flush() methods of the stream implementation. All the rest of the members tend to be plain jane passthrough stream implementation code without much consequence.

I do love the way Action<t> and Func<T> make it so easy to create the event signatures for the various events – sweet.

A few Things to consider

Performance

Response.Filter is not great for performance in general as it adds another layer of indirection to the ASP.NET output pipeline, and this implementation in particular adds a memory hit as it basically duplicates the response output into the cached memory stream which is necessary since you may have to look at the entire response. If you have large pages in particular this can cause potentially serious memory pressure in your server application. So be careful of wholesale adoption of this (or other) Response.Filters. Make sure to do some performance testing to ensure it’s not killing your app’s performance.

Response.Filter works everywhere

A few questions came up in comments and discussion as to capturing ALL output hitting the site and – yes you can definitely do that by assigning a Response.Filter inside of a module. If you do this however you’ll want to be very careful and decide which content you actually want to capture especially in IIS 7 which passes ALL content – including static images/CSS etc. through the ASP.NET pipeline. So it is important to filter only on what you’re looking for – like the page extension or maybe more effectively the Response.ContentType.

Response.Filter Chaining

Originally I thought that filter chaining doesn’t work at all due to a bug in the stream implementation code. But it’s quite possible to assign multiple filters to the Response.Filter property. So the following actually works to both compress the output and apply the transformed content:

WebUtils.GZipEncodePage();

ResponseFilterStream filter = new ResponseFilterStream(Response.Filter);
filter.TransformString += filter_TransformString;
Response.Filter = filter;                

However the following does not work resulting in invalid content encoding errors:

ResponseFilterStream filter = new ResponseFilterStream(Response.Filter);
filter.TransformString += filter_TransformString;
Response.Filter = filter;

WebUtils.GZipEncodePage();

In other words multiple Response filters can work together but it depends entirely on the implementation whether they can be chained or in which order they can be chained. In this case running the GZip/Deflate stream filters apparently relies on the original content length of the output and chokes when the content is modified. But if attaching the compression first it works fine as unintuitive as that may seem.

Resources




West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2010