Tuesday, October 26, 2010

Smart Power Control

I setup an SSH server at home running on my Media Center box. ALL of the shows that I am recording runs at night, so during the day, other than the time it has to download updates or running some batch process, it usually goes to suspend/sleep mode - which of course makes my SSH to become unavailable.

Why not just turn on "Wake up on LAN"? Well, I tried that and it worked, but it also somehow wake up the PC when I don't want it - like in the middle of the night or on a Saturday morning, or several seconds after it goes to sleep. In the end, I basically turn-off "Wake up on LAN" and trying to find a different solution.

Basically, what I want is this:

  1. The PC needs to be on between 8am to 5:30pm - this the window where I usually need my SSH
  2. It also needs to be recording my scheduled recording in Media Center
  3. Other than those, it should be in suspend/sleep mode
A friend of mine suggest moving the SSH into a router running DD-WRT. This is probably the best solution, so I can operate my PC on "Power Saving" mode and just leave the router on. Since I am using a Linksys router that is compatible with DD-WRT, I tried it. The problem is that my router does not have much memory, so not only I have to put the micro_plus_ssh version, but also when SSH is running the router would lock up and I have to reboot it. So after playing with this option for about 2 days, I went back to running my SSH on my Media Center box. Maybe one day when I am buying a new router - I will get one that is much more capable to run SSH on. But now back to original plan.

So for a while I just put my PC to "Never" sleep and just turn it off at night and turn it back on in the morning. I moved all my scheduled maintenance stuff (virus scan, windows update, etc) to morning/afternoon. Obviously this gets to be a pain after a while - especially when I keep forgetting to turn it on or off.

So for now, I use a software called "SmartPower" - where I schedule my PC to be "ON" between certain times and/or when certain parameters are set. Here is an article in LifeHacker about it. The way to use it is basically setting your computer to "Never" sleeps, and let SmartPower take over the sleep management (instead of letting Windows doing it). So, to meet my needs, I set:
  1. In "Schedules" tab to awake / stay on during the weekdays between 8:15am to 11:55pm
  2. Under "CPU" tab, set to 27% - this is for watching recordings past midnight or on weekends
  3. I set my PC to awake when Media Center remote is used
That's it - works flawlessly. Oh - and I replaced the power supply unit into the one that is 80 Plus certified to conserve more power during idle or low processing time while it's on.

Additional readings:
-- read more and comment ...

Thursday, October 7, 2010

Conditional Reference in VS 2010

In one of our projects, we are using some third party components that have separate dlls for 32 bit and 64 bit. Since our old development box and production server were 32 bit, so we used the 32 bit version. No problem whatsoever.

In a different project for different client, our development box is a 64 bit machine. But, since we are running VS 2010 and using Cassini for our debugging, we use the 32 bit reference. But since our production box is running 64 bit, last minute before build for release in our build server, we swap the reference to 64 bit, re-check-in and build. So we just keep doing it that way - until one day, we forgot to do that and a production build got pushed to production environment and (of course) it breaks. So we fix it and then we vowed to find a solution to the last minute reference swap thing.

So here is how we fix it:
In VS, right click on the project that has the reference to the 32/64bit ref, do an unload project and open using XML editor and in it, you will see something like this:
...
    <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    </PropertyGroup>
...

    <ItemGroup>
        <Reference Include="Project.Reference.MyProject">
            <HintPath>..\References"Project.Reference.MyProject.40.x86.dll</HintPath>
        </Reference>
    </ItemGroup>
...
What you need to do is add a condition in your project file, so on certain environment it will use the x86 and x64 for other. The easiest way to do this (which is what we did) is adding a conditional statement itself on the reference element - when compiled as "debug", use the x86 dll and to use the x64 when compiled as "release". So we changed it to be like this:
 
...
    <ItemGroup>
        <Reference Include="Project.Reference.MyProject" Condition="'$(Configuration)' == 'Debug'">
            <HintPath>..\References\"Project.Reference.MyProject.40.x86.dll</HintPath>
        </Reference>
    </ItemGroup>
    <ItemGroup>
        <Reference Include="Project.Reference.MyProject" Condition="'$(Configuration)' == 'Release'">
            <HintPath>..\References\"Project.Reference.MyProject.40.x64.dll</HintPath>
        </Reference>
    </ItemGroup>
...
-- read more and comment ...

Tuesday, September 14, 2010

Session Timeout Settings in IIS7

We did a release to a production environment running IIS7.x, .NET 4, MVC2, etc. Everything went smoothly and after several hours, we started to get calls about time-out issue. Our users want the time-out to be 1 hour (the current setting) which is obviously longer than 20 minutes (which is the default setting).

I remember changing and rechecking this parameter value in the web.config and also in the IIS box, both in our test and production environments. So we double checked and everything was setup correctly - so we did our own test. After 10 minutes, no time out - as expected. But on the 20-25 minutes test, we did get a time out. We triple-check our web.config and application pool setting - they are all setup correctly.

So, apparently, I missed one other time out setup in IIS - the cookie time out. Basically there are 3 (or 4 depending on your authentication setup) settings that need to be managed. IIS will use the lowest value. The main 3-4 settings that I have to change are:
  1. Application Pool - - Advanced Settings Menu - Process Model - Idle Time-out (minutes)
  2. Sites - - Session State - Cookie Settings - Time-out (in minutes)
  3. If you are using State Server or SQL Server to manage your session (instead of InProcess), you will need to set these values up too: Sites - - Session State - Session State Mode Settings
  4. Under web.config - system.web - authentication mode - forms - timeout (for form authentication)
-- read more and comment ...

Thursday, August 26, 2010

VS 2008 locking up after Office 2010 install

I installed MS Office 2010 about 3 weeks ago and everything went smooth. For the most part, I have been developing in VS 2010, but lately, when I was doing code review, I have to a .NET 3.5 project in VS 2008 - and it locks up on me almost every time.

I thought it was some extension I installed or something screwed up on my project. So I uninstall all extensions, but it was still locking up. I open the project on a different computer, it was running fine, same source and references and all that. I went to search on the internet and found out that there are some issues between VS 2008 and Office 2010 installation. One my particular issue, all I need to do is to rerun "Microsoft Visual Studio Web Authoring Component".

The setup.exe file is located at : C:\Program Files (x86)\Common Files\microsoft shared\OFFICE12\Office Setup Controller\.

So just shut down all VS 2008 instance and any Office programs and then run the Setup.exe and it should be good to go - at least that did it for me.

Credit to Martin Hinshelwood!
-- read more and comment ...

Wednesday, August 25, 2010

StringLength validator into MaxLength attribute via Html Helpers

Let's say you use Data Annotation validations and attributes in your object classes, such as this:

[StringLength(50, ErrorMessage = "Title may not be longer than 50 characters")]
public string Title { get; set; }

[DisplayName("First Name")]
[Required(ErrorMessage = "First Name is required")]
[StringLength(100, ErrorMessage = "First Name may not be longer than 100 characters")]
public string First { get; set; }

[DisplayName("Last Name")]
[Required(ErrorMessage = "Last Name is required")]
[StringLength(100, ErrorMessage = "Last Name may not be longer than 100 characters")]
public string Last { get; set; }

Then you setup your view:
<table>
<tr>
    <td class="fieldLabel"><%:Html.SmartLabelFor(m => m.Title)%></td>
    <td class="fieldValue"><%:Html.EditorFor(m => m.Title)%>
    <%:Html.ValidationMessageFor(m => m.Title)%></td>
</tr>
<tr>
    <td class="fieldLabel"><%:Html.SmartLabelFor(m => m.Last)%></td>
    <td class="fieldValue"><%:Html.EditorFor(m => m.Last)%>
    <%:Html.ValidationMessageFor(m => m.Last)%></td>
</tr>
<tr>
    <td class="fieldLabel"><%:Html.SmartLabelFor(m => m.First)%></td>
    <td class="fieldValue"><%:Html.EditorFor(m => m.First)%>
    <%:Html.ValidationMessageFor(m => m.First)%></td>
</tr>
</table>
Everything works great, the validators are validating, the display name shows up correctly, etc. Then you realize that it would have been much more awesome if the textbox is also limiting the entry to the StringLength specs using the maxlength attribute of the input element. It should not be that bad, right? Pretty easy? Just make or modify an Html helper and display another attribute based on a validation value - right? Nope ... turns out it is quite a challenge.

The issue is that the ModelMetadata does not carry all the attributes. So in our case, the validator attribute for StringLength is nowhere to be found. Well, not really, it's in there, but we have to do a little bit more digging.

The ModelMetadata has all the validators, so we can get them. Once we get all the validators, it's only a matter of getting the one we want, which is the one associated with StringLength, then it's all a matter of appending the right Html attribute the the element. Here is the completed code (named string.ascx, located in /Views/Shared/EditorTemplates/):
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<%
    System.Collections.Generic.Dictionary<string, object> attributes = new Dictionary<string, object>() { { "class", "text-box single-line" } };
    IEnumerable<ModelValidator> validators = ModelValidatorProviders.Providers.GetValidators(ViewData.ModelMetadata, ViewContext);
    ModelClientValidationRule stringLengthRule = validators.SelectMany(v => v.GetClientValidationRules()).FirstOrDefault(m => m.ValidationType == "stringLength");
    if (stringLengthRule != null && stringLengthRule.ValidationParameters.ContainsKey("maximumLength")) {
        attributes.Add("maxlength", stringLengthRule.ValidationParameters["maximumLength"]);
    }
    Response.Write(Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, attributes));
%>
Additional readings:
-- read more and comment ...

Sunday, August 8, 2010

Restaurant Review: Basi Italia

Helen and I went to an Italian restaurant for her birthday called "Basi Italia". In short: it was probably the best Italian food experience we've had. Highly recommended. We are looking for opportunities to return there.

It is located in Short North area, on Hihgland St. Click here for map & other reviews.

The place is small. I don't think there is enough room for 25 people inside. But, they have a pretty nice outdoor setup - so there is more room outside. It is a more intimate setting and small.

Must get: the zucchini appetizer. We picked it because it was unique on the menu and how we did not regret. It was awesome. It is julienned zucchini with pecans tossed with light vinegar based oil with sliced cheese. Extra yummy.

For entrĂ©e,  I got the special for that day, which is ravioli two ways with 2 different sauces. Best ravioli I have ever had. I even forgot the name ... but it was a special thing, so not on the menu.

Helen got the seafood pasta and it was excellent. Good (not huge) portion with plenty of scallops, shrimps, and clams.

Overall, we thoroughly enjoyed our experience there and certainly will return!
-- read more and comment ...

Thursday, July 15, 2010

Html.SmartLabelFor

I was really happy with the "EditorFor", "LabelFor" etc helpers in MVC2 and I have been using them all over the place in my project. Most of my models have some kind of required fields in them and although they have been validating correctly and displaying the error messages correctly, I want to notify the user that the field is required on the initial form. Usually this is done by giving some kind of visual indicator for the required fields, such as "*" or making the label as bold. In my initial attempts, I just add "*":


        <div class="editor-label">
            <%: Html.LabelFor(m => m.FullName) %> *
        </div>
        <div class="editor-field">
            <%: Html.TextBoxFor(m => m.FullName) %>
            <%: Html.ValidationMessageFor(m => m.FullName) %>
        </div>

After looking at it for a while, it looks ugly. It worked, but I want a more elegant solution and I made an Html Helper.

What I want it to do is basically appending an "*" next to the label, ONLY IF the field is marked as REQUIRED, so I can do this:

        <div class="editor-label">
            <%: Html.SmartLabelFor(m => m.FullName) %>
        </div>
        <div class="editor-field">
            <%: Html.TextBoxFor(m => m.FullName) %>
            <%: Html.ValidationMessageFor(m => m.FullName) %>
        </div>

If "FullName" is attributed with "Required", it will render "Full Name *" but will just render "Full Name" if it's not marked "Required".

Without further ado, here is the code - pretty simple:
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Linq.Expressions;

    public static class LabelExtensions {
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcHtmlString SmartLabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
            return LabelHelper(html,
                               ModelMetadata.FromLambdaExpression(expression, html.ViewData),
                               ExpressionHelper.GetExpressionText(expression));
        }

        internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName) {
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (String.IsNullOrEmpty(labelText)) {
                return MvcHtmlString.Empty;
            }

            if (metadata.IsRequired) labelText = labelText + " *";

            TagBuilder tag = new TagBuilder("label");
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
            tag.SetInnerText(labelText);
            return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
        }
    }
}
Additional readings:
-- read more and comment ...

Wednesday, July 7, 2010

Html Helper for Model Binding in MVC2

In ASP.NET MVC2, you can create a model and bind its properties to the UI by using "DisplayFor" or "LabelFor" or "EditorFor". To illustrate, let's take a look an example. For instance, if I have a model:

 
    public class Person {
        [Required]
        [DisplayName("Full Name")]
        public string FullName { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Email Address")]
        public string Email { get; set; }

        [DataType(DataType.MultilineText)]
        [DisplayName("Address")]
        public string Address{ get; set; }
    }  
To create an editor for the model will look like this in the old way of MVC:
   

        <div class="editor-label">
            Full Name 
        </div>
        <div class="editor-field">
            <%=Html.TextBox("FullName") %>
            <%=Html.ValidationMessage("FullName") %>
        </div>

With the new "DisplayFor", "EditorFor" and "LabelFor" etc, we can transform that code into something like this - and it will render the same way:
  

        <div class="editor-label">
            <%: Html.LabelFor(m => m.FullName) %>
        </div>
        <div class="editor-field">
            <%: Html.TextBoxFor(m => m.FullName) %>
            <%: Html.ValidationMessageFor(m => m.FullName) %>
        </div>

This code is much cleaner, flexible, easier to maintain, and validation is centralized in the business layer. So for example, if for whatever reason the label should be changed to "Name" instead of "Full Name", all we need to do is change the "DisplayName" attribute in the model declaration.

Now for the address field, we attributed "DataType(DataType.MultilineText)" to it. This will automatically render out a text area instead of a regular text box.

If we do not like the way the default template render, we can always override it. Brad Wilson has an excellent blog post series about this.
-- read more and comment ...

Sunday, June 13, 2010

Using T4MVC in ASP.NET MVC2

For the last 2 months, I have been using T4MVC for my MVC2 projects - and it is awesome! Previously, to provide strongly typed view name/path, I have to create a helper class that list all of my view names and location, so when I refer to them I can just type:

<% Html.RenderPartial(ViewLocations.MyCustomview);%>
This is handy, but every time I create new view or rename it, I have to be diligent in renaming it in my helper class. With T4MVC, all this are done automatically!

You can get T4MVC template via MVC Contrib project in Codeplex. How to use it - after the jump.

To use it, you will need to follow these easy steps:

  1. Download T4MVC (consisting of 2 files: T4MVC.tt and T4MVC.settings.t4)
  2. Put the files at the root of your ASP.NET MVC project
  3. That's it basically. If you need to regenerate code, right click on the T4MVC.tt file and click "Run Custom Code"
Here are some of the benefits of using T4MVC template that I heavily depending upon now. For more examples, click here for T4MVC docs on Codeplex.

View Name Constants
Instead of writing this:
 <% Html.RenderPartial("~/Views/MyController/MyCustomView.ascx");%>
I can now write:
 
<% Html.RenderPartial(MVC.MyController.Views.MyCustomView);%>
  
It even works if you have deeper folder structure, so instead of:
  
<% Html.RenderPartial("~/Views/MyController/Controls/MyCustomView.ascx);%>
  
Now I can do this:
 
<% Html.RenderPartial(MVC.MyController.Views.Controls.MyCustomView);%>
  
Also wonderfully works with Area:
 
<% Html.RenderPartial(MVC.MyArea.MyController.Views.Controls.MyCustomView);%>
  
This obviously also works from inside the controller,so instead of:
return View("~/Area/MyArea/Views/Controls/MyCustomView.ascx");
Now:
return View(MVC.MyArea.MyController.Views.Controls.MyCustomView);


Controller Actions
Previously in MVC you have write something like these:
<% =Html.ActionLink("Click here", "MyAction", "MyController")%>

<% =Html.ActionLink("Next Page", "GoToPage", "MyController", new { id = 1, page = 2, active = true })%>

Now with T4MVC magic, they become:
<% =Html.ActionLink("Click here", MVC.MyController.MyAction())%>

<% =Html.ActionLink("Next Page", MVC.MyController.GoToPage(1, 2, active))%>

Or if you have to add additional parameters that are not in the signature of the action:
<% =Html.ActionLink("Click here", MVC.MyController.MyAction().AddRoutevalue("param1", "John"))%>

<% =Html.ActionLink("Next Page", MVC.MyController.GoToPage(1, 2, active).AddRouteValues(new { sortField = "Name", sortDirection = "asc" }))%>


Using it in BeginForm
<%
using (Html.BeginForm(MVC.MyController.Edit(Model.Id), FormMethod.Post)) {
...
}

%>
There are much more usage of T4MVC that will make your MVC coding much more easier. What I have shown you are just the stuff I used the most. I recommend you to check out the docs in Codeplex for more examples.
-- read more and comment ...

Wednesday, June 9, 2010

jQuery 1.4.x Ajax traditional option

I have a very simple jQuery code that basically sends an array of integer back to my ASP.NET MVC Controller class.

 
$.post ("/MyController/MyAction", { myArray: ids });
My controller action:
 
public void MyAction (int[] myArray) {
   // do stuff here ...
}
But upon upgrading to jQuery 1.4.x, it does not work anymore. The param "myArray" in MyAction is always null.

During debugging, I found out that somehow it sent the post data as "myArray[]" instead of "myArray". Of course, you cannot name a parameter name with brackets in C# so simply renaming the parameter name in MyAction won't work.

After some readings, it turns out that this is a change in the jQuery 1.4 release - it changes the way it does param serialization. But, jQuery also provides a new flag in $.ajax to override the default behavior (and go back to use the old way of param serialization): the "traditional" option in $.ajax. So here is my revised code:
 
$.ajax({
   type: 'POST',
   url: "/MyController/MyAction",
   data: { myArray: ids },
   traditional: true
});
You can read more about the new param serialization here and here (under "Nested param serialization" section).
-- read more and comment ...

ASP.NET MVC2 Editor Template for DateTime with jQuery datepicker

Equipping a textbox with datepicker using jQuery is super easy. Once all the jQuery javascript files are referenced, all you need to do is this:

 
$(document).ready(function () {
    $("#myTextbox").datepicker({
        showOn: 'focus',
        changeMonth: true,
        changeYear: true
    });
});
So now, with EditorTemplate in ASP.NET MVC2, this gets easier. So basically I can override the default EditorTemplate for System.DateTime class (which just returns a regular text-box). Once the EditorTemplate is overridden, every time there is a DateTime in edit mode, it will be displayed using my template - which display a smaller text-box and pops up a datepicker once is focus.

Here is my EditorTemplate, located at /Views/Shared/EditorTemplates/DateTime.ascx:
 
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>" %>
<%=Html.TextBox("", (Model.HasValue ? Model.Value.ToString("MM/dd/yyyy") : DateTime.Today.ToShortDateString()), new { @class = "UseDatePicker date-text-box" })%>
Now, to declare the datepicker binding once and for all, I moved it to my master page - and instead of using the "id" of the element, use the class as the selector:
 
$(document).ready(function () {
    $(".UseDatePicker").live('click', function () {
        $(this).datepicker('destroy').datepicker({
            showOn: 'focus',
            changeMonth: true,
            changeYear: true
        }).focus();
    });
});
Now adjust the CSS for width of the text-box and other styles:
 
.date-text-box { width: 6em; }
UPDATE: Get the fully working sample project here.

Additional readings:
-- read more and comment ...

Monday, June 7, 2010

VS 2010 Coded UI Test

Last year, ASP.NET Test team release ASP.NET Lightweight Automation Framework and I blogged about it here. With VS 2010 Ultimate, you now can use "Coded UI Test" - which I think replaces it as a whole. The Coded UI Testing minimizes coding and recorded your actions - while also allowing coding and customization.

So how it works is basically like this:

  1. It records your behaviors (on a browser/WPF/Silverlight etc application) 
  2. Then it tries to guess what you are doing (moving a mouse, hover, waiting, clicking a button, typing text, etc).
  3. Generate code to replicate your behaviors/actions based on its guess. 
  4. You can also create assertions, to check whether certain elements (like texbox) has certain value, some labels display certain text, etc.

I have to say that for my web application that I am working on, it does an excellent job. It does all the low end work of recording my actions - and all I have to do is string them all together into a coherent sequence of actions in my tests.

MSDN has an excellent walk through on how to start one and customizing it to fit your needs and I am not going to repeat it in my post. You can read the MSDN how-to here.

In my experience in using it, it has its quirks:
  1. In some AJAX scenarios it will retain the old value or view of certain actions. So let's say you are looking at a product administration "Detail" screen and you click "Edit" to bring up the editing capability. Upon save, your application then brings you back to the "Detail" screen. In this scenario, sometimes upon "Save", the Coded UI test still holds the old "view" of the "Detail" screen instead of reloading/refreshing with the new "Detail" screen with the new/updated values. As a result, when you have an assertion to check whether certain values are updated, it will fail.
  2. Updating and deleting an assertion test or a recording is quite difficult. You have to manually delete it in code all the way. Updating certain test is also quite difficult if you want to do it manually. So for example if I have a test that checks whether a particular textbox value is "Something" and I want to change it to check for "some things" instead, intuitively I went to look for the line of generated code and change the checking value. But upon running, the code is regenerated and my changes are reverted back to "Something".
  3. The generated code also combine most of the generated code into 1 UI map. Which gets huge once you start building multiple tests. MSDN has a walkthrough on how to build tests using multiple UI mappings, but it is quite a challenge.
Beyond those issues that I have experienced so far, the tool itself is pretty impressive and I like it.

Some more readings and how-tos from MDSN

-- read more and comment ...

Saturday, June 5, 2010

How to Suppress .NET Warnings

In my current project, there are several warnings (C#) that are caused by several lines of code resulting from CSLA inheritance. Those warnings are basically about a local variable that is assigned but never used. Completely harmless and localized.Or on a different scenarios, you may run a Code Analysis and it returns a bunch or warnings.

For the longest time, I just ignore those warnings. But when I moved from VS 2008 to VS 2010 and to .NET 4, I thought that now is the right time to suppress those warnings.

There are several ways to remove/suppress warnings:
  1. Obviously, the warnings are there for a reason, and maybe that reason is valid. Maybe you declare a variable that is never used and should be removed, or make some methods "static", or unreachable code, etc etc. If these warnings are valid - then you should fix your code.
  2. Please do not go do this step if you have not done #1 thoroughly. OK, now you decided that the code is correct but the warning is still there and you want to suppress it. In most cases, you can right click on the warning in the list and select "Suppress Message" and pick whether you want to suppress it inline in the code or put them in a separate GlobalSuppression.cs file. Only the warnings that have a "CA" number or "CheckId" can be suppress this way. Warnings that do not have CheckId must be handled in a different manner - see #3.
  3. Using #pragma. Read this for MSDN explanation of pragma. Basically, with pragma, you do this: #pragma warning [disable|restore] [CheckId]. The CheckId must not contain the "CA" prefix. 
There you go ... now we can sleep a bit better knowing we have less warnings in our code. 
-- read more and comment ...

Sunday, May 16, 2010

Bundling Javascripts & CSS Files

If you are using jQuery for your site, it is possible that eventually your page header section may look like this:

 
<link href="stylesheets/ui.all.css" rel="stylesheet" type="text/css" />
<link href="stylesheets/admin.css" rel="stylesheet" type="text/css" />
<link href="stylesheets/maincontent.css" rel="stylesheet" type="text/css" />

<script src="scripts/jquery-latest.min.js" type="text/javascript"></script>
<script src="scripts/ui.core.js" type="text/javascript"></script>
<script src="scripts/mycustom.js" type="text/javascript"></script>
Now in those lines of code, there are 6 requests being made separately to the server and this can become a bottle neck for your site performance. Both YSlow and PageSpeed (read here and here) recommend reducing the number of requests being made as a way to create a high performance site.

Easy, right? Just combine the files with a simple text editor - both javascript files and css files are plain text anyway! Yes - you can do that. But this approach will yield a nightmare-ish maintainability and just plain ugly.

Justin Etheredge made a small framework to precisely do this - he called it Bundler.Framework. You can read his latest blog post about it here and download the code here. It is very simple to use and I have had excellent results with it. The framework itself is pretty self-explanatory and Justin's posts highlight on how to use it pretty well.

Through this post, I just want to highlight some of the small things I have to do to eventually get it working (and it has been working awesomely).
  • Add a refence to Bundler.Framework.dll
  • For the code to actually create a single js file (or css), you will need to make sure that your site's compilation is configure to NOT debug. You can do this in web.config, or else your js files will just be listed on one by one instead of being combined:
    <compilation debug="false" targetFramework="4.0">
    
  • Bundler also provide options to compress your css and also to minify your js with different minifiers.
     
    <%
        Response.Write(
            Bundler.Framework.Bundle.Css()
                .Add("~/stylesheets/ui.all.css")
                .Add("~/stylesheets/admin.css")
                .Add("~/stylesheets/maincontent.css")
                .WithCompressor(Bundler.Framework.Css.Compressors.CssCompressors.YuiCompressor)
                .RenderOnlyIfOutputFileMissing()
                .Render("/JJCMS/Content/Stylesheets/combined.css")
        ); 
    %>
    <compilation debug="false" targetFramework="4.0">
    
  • Bundler also provide options to only render combined js or css file if the rendered file is non-existent. You use the "RenderOnlyIfOutputFileMissing" option. See code above for example.

So now, with Bundler, your js and css will be rendered like this:
 
<script type="text/javascript" src="/scripts/combined.js?r=67EFAB2205D48FBBB390A6F11C8A4002E"></script>
<link rel="stylesheet" type="text/css" href="/stylesheets/combined.css?r=A84FC8E0836CD939A781066B0BBDE028" />
-- read more and comment ...

Wednesday, May 12, 2010

Installing Visual Studio GDR R2

Visual Studio 2008, by default, will work with SQL 2005 database project, BUT NOT SQL 2008 based. Microsoft provided a GDR (General Distribution Release) to make VS 2008 work with SQL Server 2008. You can work the latest version of the GDR here.

Reading the description in the download link, the requirements are VS2008 SP1, nothing CTP or Beta or Power Tools install. So, I think my box met all those requirements, and I ran the install. To my surprise, it failed and it gives me this message: "Visual Studio Team System 2008 Database Edition GDR Does Not Apply or is blocked by another condition on your system. Please click the link below for more details.".


To which I was not sure of what to do. So by reading the forums and blog postings, I found out that the installer actually write a log file, located in your local data temp file dir. Mine (running on Win 7) is located in: "C:\Users\\AppData\Local\Temp". The files should be labeled like "Visual Studio Team System 2008 Database Edition GDR ... .html". If open one of those, you should see the log generated by the installed and you can detect where it failed.

By examining the log file, my installation failed because it was looking for the SP1 flag in the registry. So even though I have SP1 installed, it did not matter, the installer is just looking in to the flag in the registry and if it does not find it, it abort the installation.

Eventually I have to run the SP1 Preparation Tool and the SP1 itself.

After the SP1 re-installation, the registry setting for SP1 is properly marked as 1 (or true). So the next step is to run again the GDR installer - and it went successfully this time. All is happy now.

Update: Gert Drapers wrote a much more extensive blog post about this here. -- read more and comment ...