LavaBlast Software Blog

Help your franchise business get to the next level.
AddThis Feed Button

jQuery plugin to postback an ASP.NET button

clock August 20, 2012 10:52 by author EtienneT

We use jQuery a lot here at LavaBlast, but we also use ASP.NET webforms. We needed a simple reusable way to cause a postback on an asp.net managed Button or LinkButton.

Here is how it would be used for <asp:Button ID=”btShow” runat=”server” OnClick=”DoSomething” />

// Cause btShow to postback to the server
$('[id$="btShow"]').postback();

If you are not too familiar with jQuery, the selector [id$=”btShow”] search for any control with an id which ends with “btShow”.

Since ASP.NET 4.0, you could also use the new ClientIDMode=”Static” property on the server control to be able to have a static ID on the client and use a jQuery selector like this: $(‘#btShow’), but this is the matter of another discussion completely.

The postback() method is a jQuery plugin which I include here:

(function ($)
{
    $.fn.extend({
        postback: function ()
        {
            return this.each(function ()
            {
                if (this && "undefined" != typeof this.click)
                    this.click();
                else if (this && this.tagName.toLowerCase() == "a" && this.href.indexOf('javascript:') == 0)
                    eval(this.href.toString().replace('javascript:', ''));
            });
        }
    });    
})(jQuery);

Feel free to use this and let us know if you find any problems with the code.



Style ASP.NET Web Forms Validators with qTip 2

clock August 13, 2012 08:20 by author EtienneT

View demo | Download source

The default validators inside ASP.NET Web Forms are quite uninteresting and require some styling work to look adequate.  Recently, we’ve been using the qTip2 jQuery library and we love it.  qTip enables you to add visually pleasant tooltips to any element.  For example, you simply add a “title” attribute to any element and then apply qTip to this element and the “title” attribute will be used as the tooltip’s text.  This is the simplest use case.  Here’s an example; with our FranchiseBlast registration form.

image

When you try to submit this form and the validation doesn’t pass, we replaced the default ASP.NET validators with styled qTip tooltips beside each validated element.

SNAGHTML6770ee44

Like you can see, the validators have absolute positioning, which enables them to flow outside of the bounds of the registration panel.  We could also easily change the position of the bubble in relation to the validated element and also change the bubble tip position.

Let’s take a look at what was needed to accomplish this, using a simple ASP.NET project. Here is the main ASP.NET code for the ASPX page.  Nothing fancy: a simple form with some validators:

Default.aspx

<asp:ScriptManager ID="p" runat="server">
    <Scripts>
        <asp:ScriptReference Path="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" />
        <asp:ScriptReference Path="~/Scripts/qtip/jquery.qtip.min.js" />
        <asp:ScriptReference Path="~/Scripts/validators.js" />
    </Scripts>
</asp:ScriptManager>
<fieldset class="Validate" style="width: 300px">
    <legend>Tell us about yourself</legend>
    <div>
        <span class="label">Business Name:</span>
        <asp:TextBox ID="txtBusinessName" runat="server" />
        <asp:RequiredFieldValidator ID="rfvBusinessName" runat="server" ControlToValidate="txtBusinessName" Text="Your business name is required" SetFocusOnError="true" EnableClientScript="true" />
    </div>
    <div class="alternate">
        <span class="label">Your Name:</span>
        <asp:TextBox ID="txtYourName" runat="server" />
        <asp:RequiredFieldValidator ID="rfvName" runat="server" ControlToValidate="txtYourName" Text="Your name is required" SetFocusOnError="true" EnableClientScript="true" />
    </div>
    <div>
        <span class="label">Your Email:</span>
        <asp:TextBox runat="server" ID="txtEmail" />
        <asp:RequiredFieldValidator ID="rfvEmail" runat="server" ControlToValidate="txtEmail" Text="Email is required" SetFocusOnError="true" EnableClientScript="true" />
        <asp:RegularExpressionValidator runat="server" ID="revEmail" Text="Invalid Email" ControlToValidate="txtEmail" SetFocusOnError="true" ValidationExpression="^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@(([0-9a-zA-Z])+([-\w]*[0-9a-zA-Z])*\.)+[a-zA-Z]{2,9})$" EnableClientScript="true" />
    </div>
</fieldset>
<asp:Button runat="server" ID="btnCreateAccount" CssClass="Next" Text="Create Account" />

Starting from the top, we need jQuery and also qTip to be added to our page.  The interesting JavaScript code in located in ~/Scripts/validators.js.  The rest of the code here is a simple ASP.NET form.  One important thing is that each element to be validated is enclosed in a <div> with his corresponding validators.  This is important because we will use this convention later in our script to find the associated validators for an input control.

I also have to mention that I added some lines in the .skin file of the App_Theme:

Default.skin

<asp:RequiredFieldValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:CustomValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:RangeValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:CompareValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />
<asp:RegularExpressionValidator runat="server" CssClass="ErrorMsg" Display="Dynamic" />

This will force CssClass=”ErrorMsg” on validators.  This will be used next in our JavaScript code to find the validators:

validator.js

Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function () {
    function getValidator() {
        return $(this).parent().find('.ErrorMsg').filter(function () { return $(this).css('display') != 'none'; });
    }
 
    var inputs = '.Validate input:text, .Validate select, .Validate input:password';
 
    var submit = $('input:submit');
 
    var q = $(inputs).qtip({
        position: {
            my: 'center left',
            at: 'center right'
        },
        content: {
            text: function (api) {
                return getValidator.call(this).html();
            }
        },
        show: {
            ready: true,
            event: 'none'
        },
        hide: {
            event: 'none'
        },
        style: {
            classes: 'ui-tooltip-red ui-tooltip-shadow ui-tooltip-rounded'
        },
        events: {
            show: function (event, api) {
                var $this = api.elements.target;
                var validator = getValidator.call($this);
                if (validator.length == 0)
                    event.preventDefault();
            }
        }
    });
 
    if (window.Page_ClientValidate != undefined) {
        function afterValidate() {
            $(inputs).each(function () {
                var validator = getValidator.call(this);
 
                if (validator.length > 0) {
                    var text = validator.html();
 
                    $(this).addClass('Error').qtip('show').qtip('option', 'content.text', text);
//                    validator.hide();
 
                }
                else
                    $(this).removeClass('Error').qtip('hide');
            });
        }
 
        $(inputs).blur(afterValidate);
 
        var oldValidate = Page_ClientValidate;
 
        Page_ClientValidate = function (group) {
            oldValidate(group);
 
            afterValidate.call(this);
 
            submit.removeAttr('disabled');
        }
    }
});

There is much to explain in this code.  First we register a new function to be executed each time there’s an ASP.NET PostBack on the page here: Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function () { … });

The function getValidator finds the visible ASP.NET validators associated to a control to be validated.  We use the fact that the control to validate and the validators are contained inside a <div>.

We apply qTip to the inputs to validate and we get the text of the message by finding the visible validators.  Also we have some logic to prevent showing the qTip element if there aren’t any visible validators.

We also do some monkey patching at the end where we inject our own code inside the Page_ClientValidate ASP.NET JavaScript method.  To do that, we simply get a reference to the Page_ClientValidate function, create a new function with our additional code (calling the old Page_ClientValidate) plus we override window.Page_ClientValidate with our new function.  This new function have both the new and old functionality.

You would probably have to modify this code a little bit to fit your needs, but this shows how you could integrate qTip2 for nicer validators in ASP.NET Web Forms.

View demo | Download source



Re-skin your existing web app using jQuery Mobile

clock May 24, 2011 15:44 by author EtienneT

IMG_3337Recently, for a new franchise client of ours, we had to add some new features to our web-based point of sale system.   This piece of software makes extensive use of touch screen functionality; we need to think about this when we design our UI.  The interface must be optimized to allow cashiers to perform their operations effectively and intuitively with a touch screen.  Our application isn’t traditionally used via a multi-touch interface like the iPhone or iPad; rather, operators use a traditional touch screen. In this project, we had to adapt existing pages and create completely new modules.

In the past, we had played with jQuery Mobile and we were really impressed.  Take a look at the demo site – all you need to do is including a reference to jQuery Mobile’s JavaScript and follow some conventions with your HTML to get a nice mobile-friendly user interface. However, when you think about it, mobile-friendly (touch friendly) user interfaces are also very appropriate for traditional touch screen technologies utilized by the retail industry for over a decade.

Given our point of sale was initially created to be compatible with IE6 back in the day, we felt it was time to enhance the look and feel utilizing new technologies. We liked jQuery Mobile’s look & feel and wanted to utilize some bits & pieces, without having to re-implement everything following their conventions.  When you peek at the jQuery Mobile source code on GitHub, you realize that each component is separated in it’s own little jQuery plugin.  Some plugins are not completely independent from the jQM (jQuery Mobile) page, but you can use most of them outside of that context; you can thus use them in your own applications without a jQM page that takes 100% of the screen real estate.  In a typical jQM scenario, you define a page and then jQM works it’s magic: it initializes all the controls for you - if you followed the conventions. In this post, we’ll cover using jQuery Mobile outside of the jQM page and mobile context.

How to trick jQuery Mobile

First, you simply have to trick jQM into thinking that there’s a jQM page in the HTML.  To do that, you have to bind to a special jQM event, mobileinit.  This is event is executed before any jQM code modifies your html:

$(document).bind("mobileinit", function ()
{
    if ($('div[data-role=page]').length === 0)
        $('<div data-role="page" id="pageTemp" style="display:none;"></div>').appendTo($('body'));
 
    $.mobile.ajaxEnabled = false;
 
    $('#pageTemp').live('pagebeforecreate', function (event)
    {
        return false;
    });
})

Here you insert a jQM page in your html if one hasn’t already been defined. This is required for some controls to work (like the dropdown list, if I remember correctly). We then disable AJAX page fetching and also disable the 'pagebeforecreate' event for the newly created dummy page.  Most of our pages utilize this placeholder (as we only use the UI controls), but we did – on a few occasions - utilize the standard jQM page in all its glory inside our point of sale. 

If there’s a better way to do this, please let us know - with the current version of jQuery Mobile (1.0a4.1) it appears to be working pretty well.

Interesting controls

The plugins we found most interesting were the one dealing with forms controls. You can see a gallery of all forms elements in jQuery Mobile here.

For example, some of the base HTML controls are not ideal in the context of a touch-enabled application. Take radio buttons, for example. They are way too small and hard to click on accurately - you have to manually change their styling via CSS to make them easy to touch.  Here is the jQM version of radio button list:

Radio Button List

radio

To create this, first you need a little bit HTML plus the checkboxradio plugin from jQuery Mobile:

<fieldset data-role="controlgroup">
    <input type="radio" name="radio-choice-1" id="radio-choice-1" value="Cat">
    <input type="radio" name="radio-choice-2" id="radio-choice-2" value="Dog">
    <input type="radio" name="radio-choice-3" id="radio-choice-3" value="Hamster" checked="checked">
    <input type="radio" name="radio-choice-4" id="radio-choice-4" value="Lizard">
</fieldset>

Basically the fieldset groups the radio input together and gives the rounded corners only to the top and bottom items.

Then you can add this JavaScript to your page:

$('input[type=checkbox], input[type=radio]').filter(function ()
{
    return $(this).parent('.ui-checkbox').length == 0;
}).checkboxradio();
$("fieldset[data-role='controlgroup']").not('.ui-controlgroup').controlgroup();

This piece of JavaScript will select all checkboxes or radios inputs, filter the ones we already applied the plugin and then call checkboxradio() to change them to follow the jQuery Mobile style.  We then use the controlgroup plugin to group the controls together visually.  Once again we don’t re-apply this code to fieldsets that were changed previously, for efficiency reasons.

For an horizontal look, simply add data-style=”horizontal” to the fieldset grouping the elements.  It is still a radio button list, but the layout is different.

radio-horiz

Checkbox list

You can even do the same thing with checkboxes:

checkboxes

Suddenly, your UI becomes much easier to use with a touchscreen.

Dropdown

Here is a dropdown list, in the jQuery Mobile style:

dropdown

You simply execute the following JavaScript code:

$('select:not([multiple="multiple"])').selectmenu();

We had some problems with multi-selection lists, so that’s why we don’t automatically style those.

Button

Buttons are pretty straightforward. In our application, we decided to automatically transform all of our buttons which utilize the CSS class ‘button’.  This could be an input[type=button], input[type=submit], a simple link <a></a>.  We’re using a mix of these in our application and modify them all like this:

$('.button').not('.ui-btn').button();

buttons2

Above, the two buttons are inline (add the data-inline=”true” attribute to the HTML elements). The Save button has a different theme (specify data-theme=”b”)

buttons

You can also group buttons as above, by defining a horizontal controlgroup.

<div data-role="controlgroup" data-type=”horizontal”>
    <a href="index.html" data-role="button">Yes</a>
    <a href="index.html" data-role="button">No</a>
    <a href="index.html" data-role="button">Maybe</a>
</div>

Working with controls

Visit this page to know operations you can do on your controls once they are modified by jQM: Form Plugin Methods. This reference is very useful if you want to enable/disable controls or refresh them with new values.

ASP.NET WebForms

If you are using ASP.NET WebForms as is our case, you want to run these plugins every time your page is modified.  If you are using ASP.NET UpdatePanels, then you can bind a function to the following event handler where you could modify your controls each time an UpdatePanel is updated:

Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function()
{
    // Add the jQuery Mobile code modifing controls here
});

Normally this job is done automatically by jQM, but since we are not using the controls inside a jQM page, we have to update the DOM manually after each postback.

Caveat

In its current form, jQuery Mobile is not compatible with Internet Explorer. Depending on the version of IE, it is easier completely unusable or doesn’t look as good (rounded corner issues). In our context (point of sale), we ended up utilizing Google Chrome Frame for our IE users – at least for the time being. The jQM team appears to be working towards full IE support for their beta release.

Future of jQuery Mobile

In conclusion, we loved working with jQuery Mobile once we figured out how to utilize bits & pieces of it individually. Currently, jQM focuses on development for mobile devices (which is normal) but we would be thrilled if they made integration into existing projects simpler. As this is an open source project, we weren’t afraid to peek at the code to figure out why it wasn’t working the way we intended it to. Let’s hope the project keeps on improving both its modularity and the desktop-based functionality.  Thank you to the jQuery Mobile team, continue the great work!



Lambda IEqualityComparer<T>

clock May 5, 2010 16:16 by author EtienneT

Just show me the code.

You’ve probably used an IEqualityComparer<T> before.  If you have, you probably know that using one is a pain because you need to implement the interface in your own class and override your Equals and GetHashCode methods.  In this post, we’ll focus on the most common scenario for checking equality: primary key comparisons.  For example, the CustomerID property of a Customer class.  This article offers a simple solution inspired by a StackOverflow post.  We’ll use lambda expressions instead of subclassing IEqualityComparer<T> for each class, thus eliminating lots of code and making everything far more readable.

A simple example should clarify the situation.  Here’s the traditional way you’d use an IEqualityComparer<T> to compare two Customer objects.

First extend from IEqualityComparer<T>:

public class CustomerComparer : IEqualityComparer<Customer>
{
    public bool Equals(Customer x, Customer y)
    {
        return x.CustomerID == y.CustomerID;
    }
 
    public int GetHashCode(Customer obj)
    {
        return obj.CustomerID.GetHashCode();
    }
}

Then you would use this new CustomerComparer class like so:

// First parameter == CustomerID
var x = new Customer(1);
var y = new Customer(2);
 
var list = new List<Customer>()
{
    new Customer(1),
    new Customer(2)
};
 
list.Contains(new Customer(1), new CustomerComparer());

As you can see, quite a bit of code just to find an object in a collection. (In this example, if we had not used our CustomerComparer, we would not have been able to find this new instance of the same customer in our list.)  Most of the time, the comparisons we want to make are pretty simple and could be expressed using a single line of code. Allow me to introduce my class (strongly inspired from this StackOverflow post) before showing a code example. This class allows us to pass a simple lambda expression to construct an IEqualityComparer<T> on the fly:

public class KeyEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> comparer;
    private readonly Func<T, object> keyExtractor;
 
    // Allows us to simply specify the key to compare with: y => y.CustomerID
    public KeyEqualityComparer(Func<T, object> keyExtractor) : this(keyExtractor, null) { }
    // Allows us to tell if two objects are equal: (x, y) => y.CustomerID == x.CustomerID
    public KeyEqualityComparer(Func<T, T, bool> comparer) : this(null, comparer) { }
 
    public KeyEqualityComparer(Func<T, object> keyExtractor, Func<T, T, bool> comparer)
    {
        this.keyExtractor = keyExtractor;
        this.comparer = comparer;
    }
 
    public bool Equals(T x, T y)
    {
        if (comparer != null)
            return comparer(x, y);
        else
        {
            var valX = keyExtractor(x);
            if (valX is IEnumerable<object>) // The special case where we pass a list of keys
                return ((IEnumerable<object>)valX).SequenceEqual((IEnumerable<object>)keyExtractor(y));
 
            return valX.Equals(keyExtractor(y));
        }
    }
 
    public int GetHashCode(T obj)
    {
        if (keyExtractor == null)
            return obj.ToString().ToLower().GetHashCode();
        else
        {
            var val = keyExtractor(obj);
            if (val is IEnumerable<object>) // The special case where we pass a list of keys
                return (int)((IEnumerable<object>)val).Aggregate((x, y) => x.GetHashCode() ^ y.GetHashCode());
 
            return val.GetHashCode();
        }
    }
}

Here is how you would use this code:

var x = new Customer(1);
var y = new Customer(2);
 
var list = new List<Customer>()
{
    new Customer(1),
    new Customer(2)
};
 
list.Contains(new Customer(1), z => z.CustomerID);

To be able to use this in Contains<T>, we need to add a new extension method (which was not included in the original StackOverflow post):

public static bool Contains<T>(this IEnumerable<T> list, T item, Func<T, object> keyExtractor)
{
    return list.Contains(item, new KeyEqualityComparer<T>(keyExtractor));
}

Once you’ve done this work for Contains, it is very easy to create extensions methods for Distinct, Except, Union, etc. Furthermore, the same principles also apply to IComparer<T>.

For a complete source code with all the classes and extension methods, you can go here: http://gist.github.com/391397

You can modify this code by forking the gist; don’t be shy to add useful methods!



BlogEngine.NET 1.6 Breaking Old Links

clock February 12, 2010 10:17 by author EtienneT

UPDATE: BlogEngine 1.6.0.1 will fix this problem

Download BlogEngine 1.6 Binary Patch (drop in your bin folder)

We recently installed an open source error logging project called Elmah on our BlogEngine.NET because we noticed that comments were broken on our blog and some people informed us of other errors.

After installing Elmah, we found out that we had a lot of exceptions like this:

The file '/post/2008/01/Attach-to-Process-with-one-shortcut.aspx' does not exist.

After some investigation, we found out that a lot of the links leading to our blog were broken!  This is bad news because we care about our Google rankings!

I investigated the BlogEngine.NET code a little bit because we considered this a major problem and found out that BlogEngine.NET changed the link pattern from:

http://blog.lavablast.com/post/2008/02/I2c-for-one2c-welcome-our-new-revision-control-overlords!.aspx  (the old pattern)

to

http://blog.lavablast.com/post/2008/02/11/I2c-for-one2c-welcome-our-new-revision-control-overlords!.aspx  (the new pattern)

The day is now required in the link. Otherwise, external links are redirected to an error page.

After investigating the code, I found out that the method RewritePost in the class BlogEngine.Core.Web.HttpModules.UrlRewrite was changed in svn revision 29143 from:

Post post = Post.Posts.Find(delegate(Post p)
{
    if (date != DateTime.MinValue && (p.DateCreated.Year != date.Year || p.DateCreated.Month != date.Month))
    {
        if (p.DateCreated.Day != 1 && p.DateCreated.Day != date.Day)
            return false;
    }
 
    return slug.Equals(Utils.RemoveIllegalCharacters(p.Slug), StringComparison.OrdinalIgnoreCase);
});
 

to

Post post = Post.Posts.Find(delegate(Post p)
{
    if (date != DateTime.MinValue &&
        (p.DateCreated.Year != date.Year || p.DateCreated.Month != date.Month || p.DateCreated.Day != date.Day))
    {
        return false;
    }
    return slug.Equals(Utils.RemoveIllegalCharacters(p.Slug), StringComparison.OrdinalIgnoreCase);
});

 

Days are now mandatory in the URL rewrite to lead to a valid post.  Our old links that were using the yyyy/mm pattern are now returning and error page, and without installing Elmah or double-checking links, I would probably not have noticed. .

The RelativeLink property of the class Post was changed at svn revision 8906 to change the pattern from yyyy/mm to yyyy/mm/dd.  This in itself was not a problem if the old links continued to work.  But with this new change to the UrlRewrite class, the legacy format no longer works.

I changed the code manually to fix our problem, but this might affect other BlogEngine.NET users as well.  If you have a pretty old blog where people link to your old blog posts, then you probably are affected.

After some investigation, this bug was introduced while fixing Bug 9212.

I made a temporary patch for those of you who run BlogEngine.NET 1.6 and are experiencing the same issue.

I changed the code in UrlRewrite to search posts a second time but ignoring days in the date if it didn’t find the post in the first search.  Here is my code:

var predicate = new Func<bool, Predicate<Post>>(includeDays =>
{
    return new Predicate<Post>(
        p =>
        {
            if (date != DateTime.MinValue &&
                (p.DateCreated.Year != date.Year || p.DateCreated.Month != date.Month || (p.DateCreated.Day != date.Day && includeDays)))
                    {
                        return false;
                    }
                    return slug.Equals(Utils.RemoveIllegalCharacters(p.Slug), StringComparison.OrdinalIgnoreCase);
                }
        );
});
 
// Search all post and include days in the search
// A valid link would look like this
// http://blog.com/post/2010/02/10/slug.aspx
Post post = Post.Posts.Find(predicate(true));
 
// We can't find this post, try to find it but ignore days in the path
// http://blog.com/post/2010/02/slug.aspx
// Some older blogengine.net site used this link pattern
// We want to preserve links to those sites.
if (post == null)
    post = Post.Posts.Find(predicate(false));

 

BlogEngine.NET developers are aware of this bug, but in the meantime, you can just drop this new version of BlogEngine.Core.dll in your bin folder to temporary fix the problem if you are using version 1.6 of BlogEngine.NET.

I highly recommend installing Elmah on your BlogEngine.NET to discover errors like these!

Download BlogEngine.NET 1.6 Binary Patch (drop in your bin folder)

kick it on DotNetKicks.com

Shout it



ASP.NET Regenerate .designer.cs

clock February 8, 2010 11:58 by author EtienneT

In this simple post, I want to share a quick tip with Visual Studio.  You probably encountered this problem before: you are designing an ASPX or ASCX form in Visual Studio and then for some reason an element you just added in the ASPX/ASCX is not available in the code behind.

If you are lucky you know exactly what you changed in the page to cause this problem and you fix it.  However, if you’re opening a file that hasn’t been worked on in a long time (maybe someone else did the most recent modification), it becomes difficult to know what exactly cause the .designer.cs automatic generation fail. There are a few common scenarios for this such as invalid HTML, missing or duplicate declarations, or unresolved references. Finding the core issue by hand is difficult because the designer does not let you know what the specific issue is.

Small Tip

To regenerate the .designer.cs file for a ASPX/ASCX, first delete the .designer.cs file, then right click on the ASPX/ASCX and click “Convert to Web Application”.  This will probably give you an error message that will help you find the root of the problem.  I noticed that there’s a difference in the error message you might get here versus when the file is opened in Design mode in Visual Studio.

For example, we had a .ASCX file where the .designer.cs file was not being generated when we modified it.  We could not figure out what the error was until we discovered this tip because Visual Studio did not give us any feedback.  Once I deleted the .designer.cs and ran “Convert to Web Application” on the file, I was prompted with an error message that informed me that we had a <%@ Register /> declaration in our ASCX that was also declared in our web.config file (and this was creating a conflict even if they both pointed to the same location).  Visual Studio could benefit from better error reporting for this particular scenario.

kick it on DotNetKicks.com

Shout it



YUI Compressor for Visual Studio

clock May 11, 2009 14:44 by author EtienneT

Although you don't want this for all things in life, you do want to ensure that your JavaScript and CSS files are as small as possible.  As a web programmer, a script minifier is a useful application that should be a part of your toolbelt. This article presents a simple way to hook up a popular minifer inside Visual Studio.

First you need to download YUI Compressor from Yahoo and unzip its contents anywhere.

External Tool

Now you want to make a new external tool in Visual Studio. (Tools -> External Tools...)

image

Then add a new tool and name it YUI Compressor.  You want to put the following values in the inputs.  Adjust the jar location depending of where you unzipped YUI Compressor.  It also assumes that java.exe is in your path.

image

  1. Title: Yui Compressor
  2. Command: java.exe
  3. Arguments: -jar "E:\yuicompressor-2.4.2\build\yuicompressor-2.4.2.jar" $(ItemPath) --charset "UTF8" --type js -o $(ItemFileName).min$(ItemExt)
  4. Initial Directory: $(ItemDir)
  5. Check “Use Output Window”

You can already test this.  Any problems will be presented in the Output window.  First you need to select your *.js file in the Solution Explorer.  Then in the Tool menu, select Yui Compressor.  Refresh the directory that contains your *.js file and you should have a file named *.min.js with the same prefix as your *.js.

You could do the same thing to minimize CSS just by making a new external tool with the –type argument value changed to css instead of js.

Toolbar

You can easily make a toolbar to quickly do this when you need it.  Go to Tools->Customize.  Then New…

image

Then go to the Command Tab and select Tools.

image

Drag and Drop the External Command 1 (or the # corresponding to your external tool if you have more than one) to the created toolbar.  Hit close and now you can put your new toolbar wherever you want.

I hope you find this useful! Setting up tools this way inside Visual Studio is a good way to speed up the development process because you don't need to memorize numerous command line parameters: everything is done with one click!

kick it on DotNetKicks.com


Co-Working Joliette

clock January 8, 2009 10:16 by author EtienneT

image Last night I had the pleasure to assist to the official opening of the new co-working space opened here in Joliette.  The mayor of Joliette M. René Laurin was there to officially launch the project along with eighty people who were there to visit, drink wine, and exchange business cards.  The new co-working space is located right on top of Librairie René Martin in downtown Joliette.

We want to encourage this kind of project because we think it encourage entrepreneurship here in Joliette.  It enables small companies to have an affordable office space and enables entrepreneurs to meet new people with different work specialities.

Readers who have followed our blog for some time now know that we have done the software for The Code Factory, a co-working space for software startups in Ottawa.  Co-Working Joliette has a different approach than The Code Factory for its pricing model and doesn't focus exclusively on software startups.  Joliette (~100,000 inhabitants) is not Ottawa or Montreal, so it doesn’t have the affluence of new people that those two cities may have, but Joliette still has many entrepreneurs who need office space and want to meet new people.  For this reason, entrepreneurs can work at Co-Working Joliette by purchasing a daily pass or rent office space on a monthly or yearly basis.  You can have your own closed office or share a work table with other people.  You can access your office 24/7, have a free parking space, and, of course, unlimited Internet access.

According to Benoit Grenier of Communication 8020, this is the second co-working space in the province of Quebec.  They already have partnerships with two other co-working spaces: one in New York, the New Worker, and one in Vancouver, WorkSpace.  They are in talks with a co-working space in London right now but nothing has been signed as of yet.

To me, the opening of a co-working location in a smaller community confirms what we've been claiming on our blog for the past two years. The Internet has made it possible to have clients all over the world, regardless of your company size and location. We've seen this first hand in the software world, but it also applies to firms specializing in marketing, translation, etc.

Good luck to all involved and let’s hope this project becomes a success in Joliette.



Declare your ObjectDataSource using lambda expressions

clock December 2, 2008 17:42 by author EtienneT

 

Example Page with Northwind | Download Source

Because SubSonic generates the business objects and the controllers in our data access layer, I often bind my controls to an ObjectDataSource. I love the ObjectDataSource but one of the most annoying things with it is that you must declare your select/update/delete method names as strings. The ObjectDataSource will call these methods via reflection, as the compiler doesn't have an explicit reference to the methods being invoked.  I found a solution to be able to set all the required properties of an ObjectDataSource simply by making a fake call on a lambda expression. Keep reading if you want to know how this works.  Simply set your SelectMethod like this:

ods.SetSelectMethod<ProductController>(ctrl => ctrl.FetchAll());

What are the issues with declaring names using strings?

Here is a typical ObjectDataSource declaration in your ASPX/ASCX:

<asp:ObjectDataSource ID="sqlDataSource" runat="server"
SelectMethod="FetchByProductAuthority" TypeName="Generated.StoreAuthority.ItemGroupController"
EnablePaging="True" SortParameterName="sort">
   <SelectParameters>
        <asp:Parameter Name="pa" Type="String" />
        <asp:Parameter Name="search" Type="String" />
        <asp:Parameter Name="user" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>


Why is it bad to have those properties set declaratively as strings, especially SelectMethod and TypeName?  If you ever rename your class, the select method or even add/remove its parameters, you will never know at compile time that you have broken your ODS (ObjectDataSource). Code refactoring tools don't affect these declarations either, so you are stuck using Find and Replace to change the strings in your declarations. In the worst case, you'll discover at run-time that you've broken your application.

Expressions + Lambda to the rescue

I just recently learned about Expression<T> from this article and it gave me an idea.  I managed to make something work with an extension method on the ObjectDataSource class plus a bit of extra glue logic that I will cover today.  My solution is not perfect since it’s the first time I play with Expressions, but I think it’s a good start.  I'd be ecstatic if you wanted to help me improve the syntax!

Here is how you would set your SelectMethod, TypeName and select parameters on an ODS, using my newly created extension method SetSelectMethod.

ods.SetSelectMethod<ProductController>(ctrl => ctrl.FetchAll());

 

imageWhat is happening in this piece of code?  We are calling an extension method on ObjectDataSource.  This extension method takes an Expression<Action<T>> as a parameter, where T is our controller in the model-view-controller pattern.  This basically means that we can pass an expression tree to the method that will be analyzed to figure out the information we're looking for. If you’re not familiar with what I’m saying, I recommend you take a look at this great article: Taking the magic out of Expression.  The expression tree enable us to retrieve all kinds of information from the lambda expression, like the TypeName (in this case “LavaBlast.Data.ProductController”) of the object used to make the method call and the Method name itself (SelectMethod = “FetchAll”).  We can even parse each parameter used and add them to the parameter collection of the ODS.  We can give Parameters default values derived from our expression tree of the lambda expression.  More on that later.

The figure on the left represents the expression tree for the lambda “ctrl => ctrl.FetchAll()”.

The goal of the lambda expression you pass to SetSelectMethod<T> is to make a fake call to the method you want the ODS to use when they make a Select operation.  By fake call I mean the ODS won’t invoke this lambda expression because it is there only to get analyzed for information, like the object name, method name, parameters, etc. However, since we are making a valid method call with all required parameters, you will be able to see at compile time that refactored code has affected this ODS. If we ever change this method in the future and it doesn’t compile anymore, then we’ll know it here.

It’s time for a real life example of something useful.  The last piece of code didn’t even include sorting and paging. Here is an example setting our ODS to utilize sorting and paging. Note that because paging is enabled, a SelectCountMethod is required by our ODS:

ods.SetSelectMethod<ProductController>(
    ctrl => ctrl.FetchAll(String.Empty, 0, 0),
    ctrl => ProductController.GetCount());


The method called has the following signature:

public virtual ProductCollection FetchAll(string sort, int startRowIndex, int maximumRows)


This time we have two lambda expressions, one with three empty arguments (these default values are ignored but the data types help us identify a particular method) and one defining our GetCount method.  The second one references a static method, but is very similar to what we've already covered. The properties TypeName, SelectMethod and SelectCount on the ODS will be specified by our SetSelectMethod.

Analyzing Parameters

What if we want to add parameters coming from the Session or from the value of a control on the page?  Or simply a Parameter having a default value?  The expression tree can be analyzed to discover the parameters to be added to our SelectParameter collection.

First of all, I’ll begin with a simple Parameter which has a constant default value.  Here is an example of a method filtering results depending on the logged in user's username:

ods.SetSelectMethod<ProductController>(ctrl => ctrl.FetchAll(HttpContext.Current.User.Identity))


The method declaration for this FetchAll:

public virtual C FetchAll(string user)


Imagining the currently logged in user is called "Etienne", we want to insert a Parameter with a default value of “Etienne”.  We do that by executing the value of the first parameter (HttpContext.Current.User.Identity), which will return “Etienne” and then when we create the parameter for which we set the default value.  We would create a parameter like this:

var parameter = new Parameter("user", DbType.String, "Etienne");


Now this was for a parameter with constant values, but what if the values comes from the Session (SessionParameter) or a Control value (ControlParameter)?

For example, in a page, I have a TextBox named txtSearch.  It's value can be used to filter the results in a GridView according to search terms.  We can't pass txtSearch.Text as above, as this would invoke it immediately instead of informing the ODS to query the control every time it fetches the data (it would become a Parameter instead of SessionParameter). However, we can pass a special type of parameter which will be recognized by our expression parser:

ods.SetSelectMethod<ProductController>(
    ctrl => ctrl.FetchAll(    ODSHelper.Control<string>(() => txtSearch.Text),
                            String.Empty, 0, 0),
    ctrl => ctrl.GetCount(String.Empty));


The related method declaration for FetchAll is as follow:

public virtual ProductCollection FetchAll(string search, string sort, int startRowIndex, int maximumRows)


Here the main difference  is the call to “ODSHelper.Control<string>(() => txtSearch.Text)”  as the first parameter to FetchAll.  At this point, we have to remember that this is a fake call to our method FetchAll.  The method call ODSHelper.Control<string> takes a lambda representing which control and which property on this control should be used while constructing the ControlParameter that will be added to the SelectParameter collection. From the information contained in ODSHelper.Control<string>(() => txtSearch.Text), we can construct a ControlParameter as follows and insert it into the ODS SelectParameter collection:

var parameter = new ControlParameter("search", DbType.String, "txtSearch", "Text");


The first generic parameter T we pass in the method ODSHelper.Control<T> is simply the return type.  We want our lambda to compile even if it’s a fake call, so we simply make ODSHelper.Control<T> return an object of type T, which is the data type of the parameter on our SelectMethod (in this case a string).

Analyzing a Lambda Expression

From this point forward, the article raises the geekiness bar even higher, as we drill-down into how all of this is implemented in the background. If you are interested how we analyze the expression tree, continue to read.

Let’s look at what we are doing inside the extension method SetSelectMethod<T>(Expression<Action<T>> exp).  I strongly suggest you download the project at the end of the article to see all the code file at once, but I’ll paste and explain the most important parts here.

public static void SetSelectMethod<T>(this ObjectDataSource ds, Expression<Action<T>> exp, Expression<Action<T>> expCount)


This is the declaration of our extension method.  We are extending ObjectDataSource and we need an Expression for the select method and one for the GetCount method. Since we are using an Action<T> this means the lambda expression doesn’t need to return anything.  We shall now dissect this method in greater detail.

// Make sure we have a lambda expression in the right format
var lambda = (LambdaExpression)exp;
if (lambda.Body.NodeType != ExpressionType.Call)
    throw new InvalidOperationException("Expression must be a call expression.");
 
var call = (MethodCallExpression)lambda.Body;
 
// Our lambda needs to be a call to a method
if (call.Method == null)
    throw new InvalidOperationException("Expression must be a method reference.");

 

 
Those first lines simply make sure that our lambda expression is indeed a method call on an object and that we have a valid method too.  Just from the information we have so far from the expression tree, we can already set the TypeName and SelectMethod of the ODS:

ds.TypeName = call.Object.Type.FullName;
ds.SelectMethod = call.Method.Name;

 

 
The interesting part lies in the fact that we can analyze the parameters of the method call and extract parameters the ODS needs to make the call to SelectMethod.  The loop to analyze the parameters is quite big.  I’ll put it all here and explain what is happening directly in code comments.  The most important thing to understand before reading the code is that if we encounter a method call in one of the parameters, we have to check to see if it’s one of the special method call like ODSHelper.Control or ODSHelper.Session.  We do that by checking if the method have a special attribute applied to them.  If the attribute is not there, we simply execute the method so that it returns a value and we use this value as the default value of the parameter.  Here is the code for the loop:

int i = 0;
// Then from the method call reflection information, we can get the parameters
foreach (var item in call.Method.GetParameters())
{
    // Don't add any parameters that are standard fields like sort and paging fields
    if (ds.SortParameterName != item.Name
        && ds.StartRowIndexParameterName != item.Name
        && ds.MaximumRowsParameterName != item.Name)
    {
        // Get the part of the expression tree that represents this parameter
        var mexp = call.Arguments[i];
 
        DbType type = GetDbType(item);
 
        object val = String.Empty;
        
        Parameter param = null;
        Type attribute = null;
        string propertyName = String.Empty;
 
        if (mexp is ConstantExpression)
        {
            // If it's a constant expression (for example if the parameter is 0 or a string "test")
            ConstantExpression c = mexp as ConstantExpression;
            val = c.Value; // just use this value
        }
        else if (mexp is MethodCallExpression)
        {
            // The following if is a special case if we want our parameter to be a session parameter
        // We could add more special cases for ControlParameter etc.
            var m = mexp as MethodCallExpression;
            // Check to see if the method call has the Session attribute applied to it
            if (m.Method.GetCustomAttributes(false).Count<object>(y => y.GetType() == typeof(SessionAttribute)) > 0)
            {
                attribute = typeof(SessionAttribute);
                // Just get the session field name to construct the SessionParameter
                val = Execute<string>(m.Arguments[0]);
            }
            else if (m.Method.GetCustomAttributes(false).Count<object>(y => y.GetType() == typeof(ControlAttribute)) > 0)
            {
                attribute = typeof(ControlAttribute);
 
                // A method call to pass a ControlParameter looks like this:
                // ODSHelper.Control<string, string>(() => txtName.Text)
                // Argument[0] contains the lambda expression
                // We just have to get the object name and the property name
                // and we have the values required for our ControlParameter
 
                // Argument[0] contains the lambda () => txtName.Text
                // This is an UnaryExpression and we have to get the right
                // side of this expression which is txtName.Text and analyze it
                var unary = ((UnaryExpression)m.Arguments[0]).Operand;
 
                val = LambdaHelper.GetObjectID(unary);
                propertyName = LambdaHelper.GetProperty(unary);
            }
            else
            {
                // Here the expression is more complex, maybe we are calling something like a method or accessing a property
                // to get our value back.  To get the value of the parameter passed, we actually need to construct
                // a new lambda, compile it and execute it to get the value returned and passed to our method call.
                val = Execute<object>(mexp);
            }
        }
        else
            val = Execute<object>(mexp);
 
        if (val != null)
        {
            if (attribute == typeof(SessionAttribute))
                param = new SessionParameter(item.Name, type, val.ToString()); // val contains the session field name
            else if (attribute == typeof(ControlAttribute))
                param = new ControlParameter(item.Name, type, val.ToString(), propertyName); // val contains the control name
            else
                param = new Parameter(item.Name, type, val.ToString()); // val contains the value passed to the method
 
            // Here we make sure that the parameter is not already added in the collection.
            // If it's there but it doesn't have the same value, we have to remove and add it
            bool contain = ds.SelectParameters.Contains(item.Name);
            if (!contain)
                ds.SelectParameters.Add(param);
            else if (contain && !ds.SelectParameters.HasValue(item.Name, val.ToString()))
            {
                ds.SelectParameters.Remove(item.Name);
                ds.SelectParameters.Add(param);
            }
        }
    }
    i++;
}


I think the comments explain the code pretty well.  I suggest that you download the complete source code to see the code for other method calls.

SessionParameter and ControlParameter

I want to get back to the syntax I chose to specify a parameter shall be a SessionParameter, a ControlParameter or simply a plain old Parameter.  We are using two static methods from ODSHelper class:

[Session]
public static T Session<T>(string sessionFieldName)
{
    return default(T);
}
 
[Control]
public static T Control<T>(Expression<Func<object>> action)
{
    return default(T);
}

 
Basically those methods do nothing.  They only return a default value of type T.  Why are we doing that?  We need to satisfy the parameter type of the method call where they will be used.

Why is it important to return T and not simply return a string?  For example, take the following method to be used as an ODS SelectMethod:

public TestCollection FetchAll(DateTime date, string search, string user, string sort, int startRowIndex, int maximumRows)

 
You want to pass this method with SetSelectMethod<T>.  The main difference is the first parameter.  It’ll take its value from the Session. Session[“FieldName”] will return a DateTime.  Here is how we could do it:

ObjectDataSource src = MainDataSource as ObjectDataSource;
 
src.SetSelectMethod<ItemGroupController>(
ctrl => ctrl.FetchAll(
ODSHelper.Session<DateTime>("FieldName"),
ODSHelper.Session<string>(SharedSessionVariables.GetSearchField(Page)),
HttpContext.Current.User.Identity.Name, 
String.Empty, 0, 0))

 
Pay attention to the first parameter.  What is important is that our method compiles and that our expression analyzer has enough information to fill the SessionParameter properties.  When we analyze the expression tree, we’ll be able to look at the parameter passed to the method call Session and see the Session field name.

The second really important characteristic is the custom attributes that both methods have ([Session] and [Control]).  Those unique attributes are used when we walk the expression tree to know what kind of parameter we need to create.  Without them, we could not distinguish between normal method calls and those that are here to give us information in the expression tree.

SessionParameter

ODSHelper.Session<string>(SharedSessionVariables.CurrentProductAuthority)

 

or

ODSHelper.Session<string>("CurrentProductAuthority")

 
If you don’t have a Session wrapper that defines the constant session field names, you can use the second option, which is more direct. When we analyze the expression tree, we simply evaluate the expression inside and create a SessionParameter with the result of the evaluation as the session field name.

ControlParameter

ODSHelper.Control<string>(() => txtName.Text)

 
This is how we pass in a control parameter.  Once again, we have a direct reference on the control and its property. If we remove the control later, our app won’t compile. txtName is this context is simply a TextBox that we have in our page.

ControlParameter needs two main things, the name of the control and the name of the property of the control.  We can get those two things by analyzing the expression tree of the passed lambda expression and add our ControlParameter in the parameter collection accordingly.

Parameter

For parameter inside our lambda expression that we pass, for example:

HttpContext.Current.User.Identity.Name

 
The expression analyzer simply executes this code and sets the returned value as the default value of the parameter.

Other Parameter Types

Right now my code is only supports those three kinds of parameters, but it would be pretty easy to add the other types of parameters (Query, Cookie, etc) with a little bit more work. Furthermore, the same logic could apply to the Update/Insert/Delete methods used by the ODS.

Conclusion

We need your opinion!  I think this project could be pretty useful.  The syntax is not very elegant, but I can't see a better way of doing this at this point.  Please download the project, play with the code, and give us your feedback! Ideally, this is something that would be built into the framework at a later date, but in the meantime we can still play around with it! This might not be the only way to solve the issues we are tackling, and if you have any other suggestions, let us know! (Example: Extending the ODS for each controller?)

Example Page with Northwind | Download Source

kick it on DotNetKicks.com



SQL Server - Restore a database backup via the command line

clock October 14, 2008 12:45 by author EtienneT

image Anyone who's ever developed a web application in .NET has had to play with a database management system, most probably SQL Server or its free cousin, SQL Server Express.  One of the tasks I personally hate doing with our SQL Server Express 2005 databases is restoring them from a backup, using SQL Management Studio.  We sometimes restore the point of sale database used by our customers to track down various issues or to build reports using their data as our test set. The process is not that long when you restore a backup from your own machine (restoring the MDF and LDF files to their original directory). If you restore databases from foreign systems, the process is simple only if both systems stored their databases in the same directory, which is rarely the case.

For example, I use Windows Vista x64 and our dedicated server uses a 32-bit version of Windows 2003.  Our data is stored in the default SQL Server directory, which is in the Program Files folder.  However, when using a 64-bit operating system, the program files directory is different (C:\Program Files (x86)).  Since the location of the MDF and LDF files are encoded directly in the bak file generated by SQL Server, restoring them via the command line is especially challenging when you don't control the original locations of the MDF and LDF files, nor their Logical Names.

Our goal is to be able to restore a database by executing a simple command such as this:

restore.bat LavaBlast

This command would look for LavaBlast.bak in the current directory and would restore the LavaBlast database to a default location on your computer where you want to store your MDF and LDF files.

Here is the code for restore.bat:

sqlcmd -S .\SQLEXPRESS -i attachDB.sql -v database="%1" -v root="%CD%"

We are simply calling sqlcmd (added to our path) to connect to our local instance of SQL Server Express and we are executing an SQL file (attachDB.sql) which includes two variables: database and root (the current path).

Here is the code for attachDB.sql:

USE MASTER
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[$(database)]') AND type in (N'U'))
  ALTER DATABASE $(database) SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
 
create table #backupInformation (LogicalName varchar(100),
PhysicalName varchar(100),
Type varchar(1),
FileGroupName varchar(50) ,
Size bigint ,
MaxSize bigint,
FileId int,
CreateLSN int,
DropLSN int,
UniqueId uniqueidentifier,
ReadOnlyLSN int,
ReadWriteLSN int,
BackupSizeInBytes int,
SourceBlockSize int,
FileGroupId int,
LogGroupGUID uniqueidentifier,
DifferentialBaseLSN bigint,
DifferentialBaseGUID uniqueidentifier,
IsReadOnly bit, IsPresent bit )
 
insert into #backupInformation exec('restore filelistonly from disk = ''$(root)\$(database).bak''')
 
DECLARE @logicalNameD varchar(255);
DECLARE @logicalNameL varchar(255);
 
select top 1 @logicalNameD = LogicalName from #backupInformation where Type = 'D';
select top 1 @logicalNameL = LogicalName from #backupInformation where Type = 'L';
 
DROP TABLE #backupInformation 
 
RESTORE DATABASE $(database)
FROM DISK = '$(root)\$(database).bak'
WITH REPLACE,
MOVE @logicalNameD TO 'C:\Program Files (x86)\Microsoft SQL Server\MSSQL.1\MSSQL\Data\$(database).mdf',
MOVE @logicalNameL TO 'C:\Program Files (x86)\Microsoft SQL Server\MSSQL.1\MSSQL\Data\$(database).ldf'
GO

Simply put, we are extracting the logical names (and other metadata) from the .bak file into a temporary table. We then use those values to restore the MDF and LDF to the correct location, instead of the ones specified in the .bak file.

If you want to use this script, simply ensure you change the location of your SQL Server data files (the last lines in the SQL file) and you should be good to go. Please note that in its current form, the script only supports files with one MDF and one LDF file in the database backup. Furthermore, it assumes your .bak file has the same name as the database you want to import. We could also enhance the script by automatically adding permissions to the ASP.NET user after restoring the database. Feel free to post any enhancements you make in this post's comments and I hope you'll find this script useful! Enjoy.

kick it on DotNetKicks.com


Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2017

Sign in