Tuesday, November 2, 2010

New Home

A new home has been found for my tech blog. Please visit http://weblogs.asp.net/kon.

Monday, August 23, 2010

Client-side Data Binding with jQuery Templates

Client-side data binding via templates is nothing new.  But as with all things jQuery, jQuery Templates makes it easier - more flexibility and options with less code.  I do believe that this jQuery plug-in is Microsoft's first contribution to jQuery, and it's a yummy one.  Here's a small example that shows how to bind user data to a grid:

First, define the grid:
<table id="userTable">
    <thead>
        <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
        </tr>
    </thead>
    <tbody></tbody>
</table>

Then define the template for each grid row:
<script id="tmpl_userList" type="text/html">
    <tr class="${alternate(this.data, this.dataSource)}">
        <td>${FirstName}</td>
        <td>${LastName}</td>
        <td>${Email}</td>
    </tr>
</script>
Notice that the class attribute's value is set via javascript function that takes two arguments: the data item (for this particular grid row), and the complete data source, which is a custom parameter being passed into the template (more on that later).

Alright, we are now ready to retrieve our data and bind.

Let's provide some data from the server:
[HttpGet]
public JsonResult GetUsers()
{
    JsonResult result = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet };

    List<User> users = new List<User>();

    users.Add(new User
    {
        ID = 1,
        FirstName = "Jennifer",
        LastName = "Jones",
        Email = "test1@test.com"
    });

    users.Add(new User
    {
        ID = 2,
        FirstName = "Jason",
        LastName = "Johnson",
        Email = "test2@test.com"
    });

    users.Add(new User
    {
        ID = 3,
        FirstName = "Jerry",
        LastName = "Jones",
        Email = "test3@test.com"
    });

    result.Data = new JsonResultData { Value = users };

    return result;
}

Now let's call GetUsers() via an Ajax call and bind the returned data to our grid/table:
$.ajax({
    url: 'Home/GetUsers',
    success: function (response) {
        if (response.Success) {
            var data = response.Value;

            // Use template called tmpl_userList 
            // dataSource is a custom parameter we pass into the template.
            $.tmpl('#tmpl_userList', data, { dataSource: data })
                // Inject data into table userGrid
                .appendTo($('#userGrid')
                // Define event handlers for each row
                .click(function () {
                    var item = $.tmplItem(this);
                    alert(item.data.FirstName + ' ' + item.data.LastName + ' says, "Hi!"');
                })
                // Toggle row color
                .hover(    
                    function () {
                        $(this).addClass('userRowSelected');
                    },
                    function () {
                        $(this).removeClass('userRowSelected');
                    }
                );


        }
    }
});

Background colors used for alternating and selected grid rows:
.userRowAlt
{
    background-color: #F7F8E0;
}

.userRowSelected
{
    background-color: #F2F2F2;
}

And our resulting grid:
grid data bound on the client side via jQuery template

If we examine the rendered HTML, we'll find that the template's markup basically got injected into the <tbody> content:

<table id="Table1">
  <thead>
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Email</th>
    </tr>
  </thead>
  <tbody>
    <tr class="userRow">
      <td>Jennifer</td>
      <td>Jones</td>
      <td>test1@test.com</td>
    </tr>
    <tr class="userRow userRowAlt">
      <td>Jason</td>
      <td>Johnson</td>
      <td>test2@test.com</td>
    </tr>
    <tr class="userRow">
      <td>Jerry</td>
      <td>Jones</td>
      <td>test3@test.com</td>
    </tr>
  </tbody>
</table>

And that's it really. Simple, clean, fun!

You can download the complete, fully functional solution here. The solution code is organized slightly differently, with most of the client-side script located inside the .js include. This way you can also see how the HTML markup, templates and script implementation (including data binding) can be cleanly separated and organized.

UPDATE - 10/06/2010:
This jQuery Templates is now an official jQuery plug-in.

Friday, July 30, 2010

Firefox Slowness with localhost on Vista (or XP with IPv6)

I recently encountered an issue with a web project running locally and VERY slowly loading pages (more specifically images) in Firefox. Thanks to this post by Dan Wahlin, I was able to resolve the problem.  Per his post:
It turns out that the slowness is caused by an IPv6 issue with DNS and can easily be resolved by turning IPv6 support off in Firefox while doing localhost testing.  To make the change, type about:config in the address bar, locate the network.dns.disableIPv6 setting and double-click on it to set it to true.  This does the trick for the Firefox localhost issue on Vista and everything is running fast again.

Tuesday, June 8, 2010

YouTube Deep Linking Inside FBML

There's a cool YouTube API option that let's you jump to any point in the video and start playing from there. For example, to jump 1 minute and 45 seconds into this video:
http://www.youtube.com/watch?v=-HWECQa23Cs
Just append #t=1m45s to the end of the URL:
http://www.youtube.com/watch?v=-HWECQa23Cs#t=1m45s

Now, if you want to do the same thing in your FBML Facebook application, the syntax is slightly different. Let's say you have the following FBML code that embeds a video on a page:
<fb:swf
swfbgcolor="000000" imgstyle="border-width:3px; border-color:white;"
swfsrc='http://www.youtube.com/v/-HWECQa23Cs'
imgsrc='http://img.youtube.com/vi/-HWECQa23Cs/1.jpg' width='340' height='270' />

Now instead of #t=1m45s, add &start=105:
<fb:swf
swfbgcolor="000000" imgstyle="border-width:3px; border-color:white;"
swfsrc='http://www.youtube.com/v/-HWECQa23Cs&start=105'
imgsrc='http://img.youtube.com/vi/-HWECQa23Cs/1.jpg' width='340' height='270' />

Thursday, June 3, 2010

SubSonic and Object-Relational Mapping

I love SubSonic (especially the latest 3.x version with T4 template support) and its ability to map data to model objects/data contracts.  However, I've hit a bit of a snag when it comes to mapping not just the high-level object properties, but also its (complex type) children.

Say for example I have the following classes defined:
[DataContract]
    public class User
    {
        [DataMember]
        public int ID { set; get; }

        [DataMember]
        public Role Role { set; get; }

        [DataMember]
        public Profile Profile { set; get; }

        [DataMember]
        public string UserName { set; get; }

        [DataMember]
        public string Password { set; get; }
    }


    [DataContract]
    public class Role
    {
        [DataMember]
        public int ID { set; get; }

        [DataMember]
        public string Name { set; get; }
    }


    [DataContract]
    public partial class Profile
    {
        [DataMember]
        public int ID { set; get; }

        [DataMember]
        public string FirstName { set; get; }

        [DataMember]
        public string LastName { set; get; }

        [DataMember]
        public string EmailAddress { set; get; }
    }

Then using SubSonic, I can run the following fluent query to hydrate a list of users:
List<User> users = db.Select
    .From<User>()
    .InnerJoin<role>(RolesTable.Role_IDColumn, UsersTable.Role_IDColumn)
    .InnerJoin<profile>(ProfilesTable.Profile_IDColumn, UsersTable.Profile_IDColumn)
    .ExecuteTypedList<User>();

However, the problem is that User's Role and Profile properties will remain null - projection mapping FAIL! Now I've spent quite some time googling around for a possible solution to this, thinking that maybe I'll find a "trick" of some sort to have SubSonic auto-magically hydrate all lower-level properties of my User object, without making a bunch of trips to the database and back to hydrate all properties. But I found nothing.

Ben Scheirman and his SubSonic and Projections blog post to the rescue!

First of, let me work backwards and start with the comments following this blog post. Rob Conery (of SubSonic fame) responded to Ben and suggested using ExecuteSingle(), which should map things appropriately. However, I've tried that as well:

User user = db.Select
    .From<User>()
    .InnerJoin<Role>(RolesTable.Role_IDColumn, UsersTable.Role_IDColumn)
    .InnerJoin<Profile>(ProfilesTable.Profile_IDColumn, UsersTable.Profile_IDColumn)
    .ExecuteSingle<User>();

And again, User's Role and Profile properties remained null, while all other simple type properties got set appropriately. Sadly, SubSonic alone couldn't hydrate all my object's children without some assistance.

So, I moved forward with Ben's suggested solution, and it worked beautifully (with some minor tweaks)!

First, define a custom attribute class:
    [AttributeUsage(AttributeTargets.Property)]
    internal class MapToColumnAttribute : Attribute
    {
        private readonly string _columnName;

        protected internal MapToColumnAttribute(string columnName)
        {
            _columnName = columnName;
        }

        protected internal string ColumnName
        {
            get { return _columnName; }
        }
    }

Then, define a projection mapper class, which will do all the leg work for you:
internal class ProjectionMapper
    {
        protected internal T Map<T>(IDataReader dr) where T : new()
        {
            Type type = typeof(T);
            T target = new T();
            foreach (PropertyInfo property in type.GetProperties())
            {
                foreach (MapToColumnAttribute attr in property.GetCustomAttributes(typeof(MapToColumnAttribute), true))
                {
                    object value = dr[attr.ColumnName];

                    if (!(value is DBNull))
                    {
                        property.SetValue(target, value, null);
                    }
                }
            }

            return target;
        }
    }

You'll notice there's a slight difference between this definition and the one in Ben's blog. First of all, he's referencing some ReflectionUtil, which I don't have and wasn't sure what it was. So I simply replaced the ReflectionUtil.GetAttributes() call with property.GetCustomAttributes(). Also, I added a DBNull check of the value - better safe than sorry.

Next, you must decorate your objects' properties with the new MapToColumn attribute, to map the properties to the appropriate columns (their names) returned by the database call:
[DataContract]
    public class User
    {
        [MapToColumn("ID")]
        [DataMember]
        public int ID { set; get; }

        [DataMember]
        public Role Role { set; get; }

        [DataMember]
        public Profile Profile { set; get; }

        [MapToColumn("UserName")]
        [DataMember]
        public string UserName { set; get; }

        [MapToColumn("Password")]
        [DataMember]
        public string Password { set; get; }
    }


    [DataContract]
    public class Role
    {
        [MapToColumn("ID")]
        [DataMember]
        public int ID { set; get; }

        [MapToColumn("Name")]
        [DataMember]
        public string Name { set; get; }
    }


    [DataContract]
    public partial class Profile
    {
        [MapToColumn("ID")]
        [DataMember]
        public int ID { set; get; }

        [MapToColumn("FirstName")]
        [DataMember]
        public string FirstName { set; get; }

        [MapToColumn("LastName")]
        [DataMember]
        public string LastName { set; get; }

        [MapToColumn("EmailAddress")]
        [DataMember]
        public string EmailAddress { set; get; }
    }

And finally, let's use this bad boy:
List<User> users = new List<User>();
User user = null;

SqlQuery query = db.Select
    .From<User>()
    .InnerJoin<Role>(UsersTable.Role_IDColumn, RolesTable.IDColumn)
    .InnerJoin<Profile>(UsersTable.Profile_IDColumn, ProfilesTable.IDColumn);

ProjectionMapper mapper = new ProjectionMapper();
using (IDataReader dr = query.ExecuteReader())
{
    while (dr.Read())
    {
        user = mapper.Map<User>(dr);
        user.Role = mapper.Map<Role>(dr);
        user.Profile = mapper.Map<Profile>(dr);

        users.Add(user);
    }
}

The solution is clean and relatively efficient; this makes Kon a happy boy. Again, I must give most of the credit to Ben Scheirman for his help.

Saturday, May 29, 2010

C# Extension Method Bummer

I love extension methods, which were introduced in .NET 3.5 Framework.  While trying to avoid extending everything and its grandmother, I find them very useful for very common tasks like special ToString() formatting or basic array manipulation (adding an element to an already full array).  But I was pretty disappointed to find an ugly limitation of extension methods, specifically in C#.

Let's say for example you want to create an extension method that adds an item to an array of objects while dynamically growing said array.  Note:  Sure we can do these things easily with ArrayLists or any other type of generic lists.  But say you're stuck with working with simple arrays... just go with me here.

So consider the following extension method:
public static void Add<T>(this T[] source, T newItem)
{
    // Create a new temporary array that's larger than the original
    T[] tmp = new T[source.Length + 1];

    // Copy contents of source array to temporary array
    source.CopyTo(tmp, 0);

    // Assign new item to the temporary array
    tmp[tmp.Length] = newItem;

    // Set original source to the newly grown array
    source = tmp;
}
And then you can simply add an item to an array with a single line of code:
user[] users = new user[0];
users.Add(new user());

Wouldn't it be nice if it were that simple?   ...and working?  Unfortunately, that is not the case with C#.  Remember that in C# objects are passed by value, not reference.  So the fact that we're setting source to tmp at the end of the extension method bares no impact whatsoever on the original source array outside of this method's scope.  This is one of those very rare times I say, "What the hell?!  I'd be better off doing this in VB!"

You also can't use ref keyword to pass this instance into the extension method by reference, because you're not really supplying that parameter to the method when calling it.  So there's nothing to put ref in front of.

So one alternative is to just return the new array:
public static T[] Add<T>(this T[] source, T newItem)
{
    // Create a new temporary array that's larger than the original
    T[] tmp = new T[source.Length + 1];

    // Copy contents of source array to temporary array
    source.CopyTo(tmp, 0);

    // Assign new item to the temporary array
    tmp[tmp.Length] = newItem;

    // Return the newly grown array
    return tmp;
}

But that's ugly, because now you have to assign the return value to the variable you just called a method on:
user[] users = new user[0];
users = users.Add(new user());

And that just gives me the willies.  It gets the job done, but... ugh..   I sure would have preferred to be able to alter the original object within the extension method - that way seems a lot cleaner to me.  What a bummer!

Friday, May 21, 2010

Thursday, May 6, 2010

Image Map Generator

Here's an excellent tool that helps you create an image map with hotspots/links:  http://www.image-maps.com/ - Online Image Mapper.

Just upload your image or specify its URL and start creating your areas:

You'll be provided with standard HTML-style image map code, as well as a CSS-only version of the code.

Thursday, April 29, 2010

Facebook IFrame Application With ASP.NET and MVC

There are several ways of developing a Facebook (fb) application.  Prior to deciding on your implementation method, make sure you read this article that explains the difference betwen an FBML and an IFrame application.  This particular example deals with an IFrame application developed in ASP.NET (4.0 Framework) and MVC 2.  So let's get going...

1.  Download the Facebook SDK.

2.  Start a new MVC web application project in Visual Studio and add your references for:
  • Facebook.dll
  • Facebook.Web.dll
  • Facebook.Web.Mvc.dll
3.  Create/register your fb application.

4.  Essential fb application information:

API Key and Secret are very important, because that's how your MVC app will identify itself to fb, and thus your fb session will be tracked using those two values as HTTP requests jump from Facebook to your application and back.  I recommend saving your API Key and Secret to the web.config.


5.  Configure fb application:

Make sure to set your Canvas Page URL, Canvas Callback URL and Render Method (IFrame).
The Canvas Callback URL is very important - it's the root path to your application that is hosted elsewhere.

While in Sandbox mode (explained below), you can temporarily change your Canvas Callback URL to http://localhost/kons_first_app/ - that should help you debug the application and step into your code.

Sandbox Mode:
While your application is under construction, be sure to enable Sandbox mode, to prevent other people on Facebook, with the exception of other developers (you can add devs in the Basic tab), from playing with or even seeing your half-done application.

6.  Using the fb API (provided by the SDK):


As you can see, we first have to get a handle to the fb Api.  Once we do that, the fb world is our oyster!  The above code is accessing the current user's profile user properties.

Notice the FacebookAuthorization attribute above our Action method.  Among other things, this handles the fb session authentication for you, so that you don't have to pass apiKey and secret into the GetApi() method.  Instead, they'll be retrieved automatically from the appSettings in your web.config (so don't forget to put them in the web.config!).

You might also be wondering how GetApi() is available via this instance, when we're in a class inheriting from Controller.

Fb SDK to the rescue!
Facebook.Web.Mvc provides us with a ControllerExtension class that implements GetApi(), thus saving us the headaches of.... writing more code.

However..  You'll quickly realize that to do any real/robust development and interaction with fb, this ain't gonna cut it.  For example, one difficulty I ran into was routing to a different/custom Action with the FacebookAuthorization attribute in place and IsFbml set to false (as it should always be for an IFrame app).  All requests kept getting routed to Index (I have a feeling fb session management was getting screwed up with the requests jumping from fb to my app).  However, I wanted to leverage automatic fb authentication and was dreading having to pass the apiKey and secret in every Action method.

So here's a more elegant solution... write a base fb controller class (which of course inherits from Controller) that handles all fb authentication and session management for you. Here are some minimal things you'll want to do in your base fb controller:

First things first, get a hold of your fb session:

Now that you can access your fb session, create an accessor for the Facebook.Rest.Api:


Almost forgot the constructor!  In the constructor, you'll want to at the very least set your API Key and Secret (from the web.config):


And that's it.  You can now create controllers that inherit from your base fb controller and make simple calls like this (without having to authenticate to fb every time):

7-ish.  Client-side fb stuff
I won't cover it here, but would highly recommend some light reading on XFBML and FBJS to learn how you can leverage fb UI code within your IFrame app.

Tuesday, April 27, 2010

MVC 2 blocks JSON GET requests by default

"This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet."

Welcome to MVC 2!
For security reasons, MVC 2 blocks JSON GET requests by default.  So if you really want to call that JsonResult method via a GET, you must explicitly allow it like this:
 

Friday, April 16, 2010

Replacing Notepad with Flo's Notepad2 on Windows 7

1.  Once you downloaded Flo's Notepad2, get the Auto Install Script (link available here).

Auto Install Script (batch file) should look something like this:

@echo off
TITLE Notepad2 Install Script for Complete Vista Notepad Replacement
echo.
echo Notepad2 Install Script for Complete Vista Notepad Replacement
echo Version 1.0
echo.
echo (c) My Digital Life (www.mydigitallife.info)
echo.
echo.
echo.
echo.echo Confirm to apply? (Press Ctrl-C and answer Y to terminate)
pause
echo.
echo.

takeown /f %Systemroot%\notepad.exe
takeown /f %Systemroot%\System32\notepad.exe
icacls %Systemroot%\notepad.exe /grant "%username%":f
icacls %Systemroot%\System32\notepad.exe /grant "%username%":f
IF EXIST %SYSTEMROOT%\SysWOW64 (bcdedit.exe -set loadoptions "DDISABLE_INTEGRITY_CHECKS")
copy %Systemroot%\notepad.exe %Systemroot%\notepad.original.exe
copy %Systemroot%\System32\notepad.exe %Systemroot%\System32\notepad.original.exe 
echo.
echo Original notepad.exe has been renamed to "notepad.original.exe" in its original folder.
echo.
copy notepad2.exe %Systemroot%\notepad.exe /y
copy notepad2.exe %systemroot%\System32\notepad.exe /y
echo.
echo Notepad2 installation is completed.
echo If no error occurred, Notepad2 will now replace all Notepad functions.
echo.
pause

2.  Copy the Notepad2.exe that you downloaded into the following folders:
  • C:\Windows\ 
  • C:\Windows\System32\
3.  Take ownership of the original notepad.exe in the following folders:
  • C:\Windows\ 
  • C:\Windows\System32\ 
4.  Then run the Auto Install Script.

Run notepad and you should be good to go.

Vault's file diff not working on Windows 7

This applies to Vault client v3.1.7 running on Windows 7.

If you get an error, saying "'Diff application /sgdm.exe could not be found" then first, try to download and install the latest Vault client.  If that's not an option for you, then try this:

In Vault, go to Tools -> Options -> Diff/Merge:


And replace all instances of  %VAULT_EXE_DIR%  with an absolute path to sgdm.exe.

 More info can be found here:  http://support.sourcegear.com/viewtopic.php?f=5&t=12841