Thursday, 29 November 2012

Managing Session State in Microsoft Windows Azure

Please see the following really useful blog post about session state options from web farms in Azure:

http://acloudyplace.com/...

Monday, 26 November 2012

Connecting to SQL Server Web Edition from Mgt Studio via SSH Tunnel

I am currently enjoying my familiarity with ssh tunnels, making me feel all smug and secure. Today, I ran up a new AWS SQL Server Instance and this box runs SQL Server 2008 Web Edition. I didn't particularly choose it but it was one of the pre-built appliances so decided to go with it.
I then did what I have done 10s of times and setup a new ssh tunnel via a Linux box on AWS to the RDP and SQL Server ports so I can securely remotely access the box. I do this so that only the boxes on AWS are allowed to access the database/web servers directly and anything else therefore must securely tunnel in.
Once setup, I ran up RDP with no problems but I couldn't get management studio to play ball and having a system with lots of variables just made it a little harder to track down.
This is what I ended up doing to make it work.

  1. Checked that the box was generally accessible by logging into the tunnel server and pinging the expected IP address - this demonstrated that ICMP/Echo Request is not enabled by default on the Windows firewall.
  2. Doing the same thing from the tunnel server by running nmap -PN -p 1433 which ensures that the port is opened by Windows Firewall (which it was)
  3. Logged into the new SQL box and setup the sa user and a normal web site user. sa is disabled by default so I enabled it and set the password to a known value.
  4. This was a biggie: Choose your database properties menu and change the authentication to use BOTH windows and SQL authentication, it just uses Windows by default which means you won't be able to login even with the correct sa credentials. Once changed, you must restart the SQL Server Service for the change to take effect.
  5. Setup a local tunnel (in my case using PuTTY) to redirect a local port via the tunnel server to my SQL box port 1433.
  6. Run up sql management studio and choose "connect". Put in the local address and port using a COMMA between host and port e.g. 127.0.0.1,2008 and then choose options and select TCP/IP as the connection mechanism. I would suspect named pipes would not work (and this is chosen because 127.0.0.1 is localhost and the system assumes it can connect directly).
  7. If you still can't connect, you can check the Windows firewall on the SQL box. Note it had port 1433 already open (and not locked down, you might want to tighten it up (or not)). Also note that ping - which is listed under File and Printer Sharing - Echo Request is not enabled by default and this might be useful for you when fault finding.
  8. Also, you might need to enable the SQL Server Browser service on the SQL box which for me was not enabled by default and I think is needed if you don't specify an instance name in your connect box (which I didn't). The service itself was disabled so needs to be enabled and started.
As with all fault finding, the best way is to try and reduce the number of variables involved. For instance, because I could connect via a tunnel to the new box for RDP, that told me that a lot of things were working OK and reduced it to a SQL server issue.

Friday, 23 November 2012

Bootstrap dropdownlist/combo box

Styling some web controls is not possible in the browser since they are drawn by the operating system. One such beast is the combo box a.k.a. the dropdownlist. What we want is the means to select something from a list which is then put into a text box and which can, optionally, allow you to free type something else. Something a bit like this:


It looks quite simple in bootstrap but again, it is hard to make it behave in a way that plays nice with server side code. The way I got it to play was the following:
<div class="dropdown">
            <div class="input-append">
                <input type="text" class="dropdown-toggle input-xlarge" data-toggle="dropdown" id="requiredPackage" runat="server" />
                <span class="add-on dropdown-toggle" data-toggle="dropdown"><i class="icon-chevron-down"></i></span>
                <ul class="dropdown-menu" role="menu" id="packageMenu">
                <li><a href="#" data-value="5">Free (50 users, 100 logins) free</a></li>
                <li><a href="#" data-value="1">Basic (200 users, 1000 logins) £5/month</a></li>
                <li><a href="#" data-value="2">Mid-size (500 users, 5000 logins) £10/month</a></li>
                <li><a href="#" data-value="3">Large (1000 users, 10000 logins) £15/month</a></li>
                <li><a href="#" data-value="4">Enterprise (5000 users, 50000 logins) £25/month</a></li>
            </ul>
            </div>
            
        </div>

I used a plain input with a runat server tag. This might have worked with a standard asp:TextBox but I ended up with this. I then added an asp:HiddenField that would allow me to set something on the client side and then read this on the server side. In fact, I wanted the data-value of the selection rather than the description of the selected item. data- is a way of adding customised attributes which can easily be read by Javascript. In this case, I used jQuery to handle the selection of any of the li items and then put the description into the text box and the value into the hidden field like this:
<script type="text/javascript">
        $(function ()
        {
            $('#packageMenu a').on('click', function (event)
            {
                $('#MainContent_requiredPackage').val($(this).text());
                $('#MainContent_selectedPackage').val($(this).data('value'));
            });
        });
    </script>

A few notes on this. By using jQuery 'on' function, I can apply the handler to a group of elements, in this case all the li elements (the a tags) that live under the packageMenu element. I then put the text of the element into the text box and use jQuery data() function to retrieve data-value and put this into the hidden field. This script is simply put at the bottom of the HTML page and is wired up to jQuery 'ready' function using the standard format.

Bootstrap icon button and ASP.Net

I've started using Bootstrap (from Twitter) to provide a nice polished look to my web site and also, ultimately to provide some pre-formed layouts which will help with responsiveness on mobile devices. One of the things that I immediately noticed was that, as with many Javascript frameworks, it can be difficult to get it to work nicely with ASP.Net and the postback model but a bit of fighting and I can now manage a few things.
One of the things you notice is that bootstrap is designed to play nicely with certain html elements and css makes them behave. Of course, this can mean that you can't get the server tag functionality to make callbacks.
I wanted to use a button with an icon on it to call a method server side. Normally, I could just use an asp:Button and some css and it will look OK. However, I noticed the syntax for creating icon buttons was like this:

<a class="btn" href="#"><i class="icon-align-left"></i></a>

Which you can immediately see is not going to work well with a button. Try and put something between the start and end tag of a button and you (understandably) get an error. I then tried using an asp:Hyperlink which sounds like it might work but then noticed there are no server event handlers on the hyperlink (although the button draws correctly). I didn't want the usual pain of trying to customised calls to __doPostBack and looked through the forums and found someone suggesting I use asp:HtmlAnchor which sounds like the same thing as asp:Hyperlink but lives under the HtmlControls namespace which hints at the fact it is a server representation of a standard HTML control. MSDN looked like it supported but this caused an error in Intellisense. A bit more trawling and I realised that what I needed to do was the following:

<a id="Button3" runat="server" onserverclick="btnGo_Click" class="btn btn-large btn-primary"><i class="icon-forward icon-white"></i></a>

And this is somehow wired up by asp.net by clever interpretation of the "onserverclick" event attribute so that it wires up correctly. That is how you get a bootstrap style icon button with callback!

Tuesday, 13 November 2012

Uncaught TypeError: Object [object Object] has no method 'dialog'

This one was caused in the unseen belly of an ASP.Net v4.5 web forms application.

I had two pages that were both supposed to display jQuery dialogs. One of these worked correctly and the other didn't with the above error. There were only 2 jquery files and they were added using cassette bundling and as it happens, each page was also using a different master page (which were basically the same).

After finding this answer on Stack overflow: http://stackoverflow.com/questions/8786104/uncaught-typeerror-object-object-object-has-no-method-dialog I was struck by the possibility of multiple-includes of jQuery being the problem. I looked at each of my pages and sure enough, the one that worked had only the reference that I had included via bundling whereas the one that didn't work had an additional jQuery include beneath the form. This one I hadn't added.

It turns out that asp.net adds this one which would be OK - I could remove my own reference - except that it ONLY appears when you have validation on the page which means that you can't easily workaround the problem. Since I pull in these script bundles from the master pages, I don't want to start having two sets of bundles and trying to trick each page into using the correct one. Also, if I changed a page, it might break by either removing or adding in the second reference.

There might be other better fixes but fortunately, I could set the validators on the page which wasn't working to disable client validation which then removes the need for the auto jquery reference which then means it all works again.

These kinds of things are a real shame because it is this kind of "smoke and mirrors" which is really hard to debug and which is not easily worked around. It reminds me of the horrible dialog language in Visual Studio 6 which was trying to be helpful but it just became another black box which mostly worked and occasionally produced weird results which you couldn't debug. I thought MS were moving away from this and starting to use code for everything so at least it was de-buggable. Otherwise, they could just add a static reference to jQuery in the master page and leave it there - removable if you don't need any client scripting (which is unlikely).

Tuesday, 6 November 2012

Problem with eval() and minification

After using Cassette for minification, I found a script error when I built the site in release which went away in debug. Of course, in debug, the minification is occuring and what was happening related to the following Microsoft code:
function ValidatorOnLoad() {
    if (typeof(Page_Validators) == "undefined")
        return;
    var i;
    var val;
    for (i = 0; i < Page_Validators.length; i++) {
        val = Page_Validators[i];
        if (typeof(val.evaluationfunction) == "string") {
            eval("val.evaluationfunction = " + val.evaluationfunction + ";");
        }
What is happening here is that because of the overloaded and confusing use of the evaluationfunction property, the code is executed using eval(). The problem during minification is that the variable val is renamed to something smaller, let's say 'n' and the code effectively becomes:
function ValidatorOnLoad() {
    if (typeof(Page_Validators) == "undefined")
        return;
    var i;
    var n;
    for (i = 0; i < Page_Validators.length; i++) {
        n = Page_Validators[i];
        if (typeof(n.evaluationfunction) == "string") {
            eval("val.evaluationfunction = " + n.evaluationfunction + ";");
        }
The val inside the quotes is (understandably) untouched and the error occurs because val is undefined. I tried various things like getting the MS minifier to use one of its properties which says to ignore the minification of functions that contain eval() but this didn't work, I then changed to use the Yahoo minifier (YUI Compressor) to achieve the same thing but still no dice. It looked the function was sort of left alone but some changes were still made which broke it. In the end I had to rewrite the eval statement to work in a way that was minifier friendly. I ended up with this:
function EvaluateStringFunction(func)
{
    var t;eval("t = "+func+";");return t;
}
function ValidatorOnLoad() {
    if (typeof(Page_Validators) == "undefined")
        return;
    var i;
    var val;
    for (i = 0; i < Page_Validators.length; i++) {
        val = Page_Validators[i];
        if (typeof(val.evaluationfunction) == "string") {
            val.evaluationfunction = EvaluateStringFunction(val.evaluationfunction);
        }
What you can see is that by returning the result of the evaluation via a function return, it avoids the ambiguity of the use of val.evaluation function and this is minifier friendly. It seems that the minifier will not rename the var inside the new function which means it should always match the t which is assigned to in EvaluateStringFunction().

Cassette Bundling and Minification

Introduction

It is nice when you discover features that have been added to Visual Studio which match best practice, things like bundling and minification to speed up web page downloads. However, what is not good is realising that even though it looks like these resources have cache-busting, they do not. This removes the whole ability to set long expiration times for scripts and css and only download them when they change.

Cassette

I then discovered Cassette (getcassette.net) which provides the same functionality but also includes cache busting for any changed files.
First note is the documentation is pretty weak at the moment, although the Cassette google group seems to be supported well by the author(s). The other issue, related to the first, is that it is easy to get an error in the configuration which doesn't always manifest very obviously and therefore requires some poking around to work out what is actually wrong.

Setup

Setup is pretty easy using nuget (the package manager in visual studio) this adds a few assemblies and some web.config as well as a file called CassetteConfiguration.cs. As with all these nuget packages, be careful that the correct project is selected at the top of the package manager console before getting the package.
Secondly, remove either the Cassette config from system.web (IIS6) or that from system.webserver (IIS7) depending on what you run it on. I think this was causing some issue for me but it is possible that it is safe to keep both sections in and my issues were elsewhere.

Configuration

You will see a couple of examples in the configuration class but be aware that there are lots of different flavours of the Add function and these do different things. If you get these wrong, you will generally get an internal server error which will not print on your screen and which may or may not be raised in the debugger. The best is to start one at a time and bring things in.
Specifically, you need to be careful to distinguish between directory names, file names and alias names which are the first arguments to the Add functions.

Note here that bundling is a compromise between having a single downloaded file (which requires less connections and overhead to download) but not wanting any change to any of these files to require the whole lot to be downloaded again. Per-directory is a good compromise otherwise consider unchanging content (framework js and css) vs app-specific files. You can also consider common content vs page-specific content.

Example 1 - Add everything under directory Content but keep them in individual files:

bundles.AddPerIndividualFile<StylesheetBundle>("Content");

Example 2 - Add two specific files under directory Content to a single bundle:

bundles.Add<StylesheetBundle>("Content", new[] {
                "Site.css",
                "smallmobile.css"
            });
note that the Content here is both an alias and the start of the file paths.

Example 3 - Add two specific files under Scripts where the alias name is NOT the same as the start of the paths.

bundles.Add<ScriptBundle>("MsAjaxJs", new[]{
                "Scripts/WebForms/MsAjax/MicrosoftAjax.js",
                "Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"});
note that these include the base level Scripts folder name in their paths

Example 4 - Add everything under directory Content but bundle them per sub-directory.

bundles.AddPerSubDirectory<StylesheetBundle>("Content");

Example 5 - Add the following files with a named marker to allow them to be rendered in a specific place on the page

bundles.Add<ScriptBundle>("PPMouse", new []{
                "Scripts/imageinject.js",
                "Scripts/jcanvas.min.js"},
                b => b.PageLocation = "mouse");
note this location is commonly going to be "head" or "body" to distinguish scripts which must be in the head from those that can be in the body. In this example, I used mouse because it is page specific and I don't want it rendered via the master page into every page on the site.

Referencing

Before you can render these scripts and css, you need to reference them. This causes Cassette to do various checking and processing on the files and generates their unique filenames for cache busting. Referencing is fairly straight-forward, as you can see from the following:

<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="PixelPinControl.SiteMaster" %>

<%
    Bundles.Reference("Content");            // Standard reference
    Bundles.Reference("Modernizr", "head");   // Reference from named position (head)
    Bundles.Reference("MsAjaxJs", "body");    // Two references from named position (body)
    Bundles.Reference("WebFormsJs", "body");
%>

<!DOCTYPE html>
<html lang="en">
<head runat="server">
note this doesn't render anything to the page at this position.
In order to then render the correct links, you use the Render method like the following:
<head runat="server">
    <!-- snip -->
    <%: Bundles.RenderStylesheets() %>
    <%: Bundles.RenderScripts("head") %>
In this example, we are rendering all style sheets (since we did not qualify the call to the RenderStylesheets) and then render only the scripts that are required in the head position. There is obviously a similar call in the body for the body scripts.

Page Specific Content

When using page-specific content, simple ensure your bundles are separated correctly and  then reference and render the content in the same place on your page. For example, my site uses a master page where the above code is used but I have a page which needs my mouse scripts and these are only allowed inside the content placeholders (body in my case):
<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
    <% Bundles.Reference("PPMouse", "mouse"); %>
    <%: Bundles.RenderScripts("mouse") %>