Wednesday, June 03, 2009

Facebook Connect With ASP.NET

What is Facebook Connect

Facebook Connect, first released in May 2008, is the latest Facebook Platform for development Facebook applications. It is basically a series of libraries to enable any website to allow a user to log into Facebook, and retrieve the user’s data such as profile and friends from Facebook. Two major functions provided by Facebook Connect are delegated authentication and data sharing for authenticated users.

In this article I will examine how Facebook Connect works and create some demo pages to connect Facebook.

Under the Hood of Facebook Connect: Secure Cross Domain Communication

By design, web browsers isolate pages in different domains to prevent them from peeking at each other for security consideration. This isolation model ensures websites only accessing data from their own servers. This becomes a barrier for advanced web applications that need to aggregate data from multiple domains.

Windows Live development team in Microsoft first presented the concept of Secure Cross Domain Communication using secure channel in nested iframes (http://msdn.microsoft.com/en-us/library/bb735305.aspx). The idea is that the application page (http://www.application.com/CrossDomainLogin.htm) includes an iframe (A) to connect third-party page in different domain like Facebook authentication page (http://www.facebook.com/extern/login.php), and embeds an extra iframe (B) inside iframe (A) where iframe B is in the same application domain. The result from third-party page (iframe A) can be passed to iframe A as a hash in URL, like iframeB.src=”http://www.application.com/Receiver.htm#ResultData”. Within Receiver.htm, onload JavaScript can send result data back to parent’s (iframe A) parent (CrossDomainLogin.htm) because they are in the same domain. Here the iframe B is the channel and bridge for cross domain communication. The approach is soon adopted by many developers and vendors. Facebook Connect uses the same mechanism in its implementation, refer to http://wiki.developers.facebook.com/index.php/Cross_Domain_Communication and http://wiki.developers.facebook.com/index.php/How_Connect_Authentication_Works for details. Instruction of building a Facebook Connect application is available in http://wiki.developers.facebook.com/index.php/Trying_Out_Facebook_Connect.

Register Application with Facebook Developer

A new Facebook Application is required for building any Facebook Connect website. To setup the Facebook Application, first thing is to login to Facebook and Facebook Developer Application (http://www.facebook.com/developers/), and allow the Developer access:


Type Application Name “MyFacebookConnect” and click Save and the Edit Application page will show up. The Application ID, API Key and Secret on the page base section are important and they are required for website to connect to Facebook.

Select the Connect section of the edit page, type url that you will use to connect Facebook, “http://myfacebookconnect.com” in our case. Also we input the myfacebookconnect.com as the Base Domain.

Click Save Changes, and then a new Facebook Application named “myfacebookconnect” is created, and summary page comes up showing the related data and links of the application. Now we are ready to build our website that will use Facebook Connect to login and pull out data from Facebook.

Note that in order to make the application work in development environment; we need to modify hosts entry so that the myfacebookconnect.com is pointing to local host. Add following to the hosts file located in “C:\WINDOWS\system32\drivers\etc\hosts”:
127.0.0.1 localhost
127.0.0.1 myfacebookconnect.com
Also the web application can only run in IIS, and the cross domain communication won’t work properly in Visual Studio embedded web server where a dynamic port number is used.

Simple Page Connect to Facebook

As we mentioned before, a channel page is required for cross domain communication. We create an html page named xd_receiver.htm under the ASP.NET solution:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js"
type="text/javascript"></script>
</body>
</html>
The page only references a JavaScript from Facebook without any other html tags. The JavaScript is the way Facebook Connect applications talk to Facebook servers, no matter the applications are using PHP, Java or .NET.

For a simple test purpose, we create a facebooktest.htm page that shows login status:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php"
type="text/javascript"></script>
<form>
<div>
<fb:login-button onlogin="window.location.reload()">
</fb:login-button> <div id="status"></div>
</div>
</form>

<script type="text/javascript">
function showLogin() {
document.getElementById("status").innerHTML = "You are logged in";
}
function showLogout() {
document.getElementById("status").innerHTML = "You are logged out";
}
FB.init("f81522f787ba594e567ec85424d513b5", "xd_receiver.htm",
{ "ifUserConnected": showLogin, "ifUserNotConnected": showLogout });
</script>
</body>
</html>
The Facebook Connect JavaScript library is loaded before the Form elements are created. The <fb:login-button> is the login button control for logging in to Facebook. The JavaScript call FB.init is invoked at the end of page. The init method takes three parameters, first is the Facebook Appplication API Key we saw previously during the Facebook Application setup, the second parameter is the cross domain channel file we just created, the last parameter is anonymous method (call back function) to invoke showLogin and showLogout status based on the return value of init call.

The page shows “You are logged out” when it’s first loaded. The Facebook login page pops up when the Facebook Connect button is clicked:


Type a valid email and password, and click Connect button, the popup will be closed and the facebooktest.htm page is refreshed. Now the status is changed to being logged in:


Retrieve Facebook Friend List

After a user logging in the Facebook Connect, the application has the capability to retrieve user’s profile and other information.

In this example, we create an ASP.NET application to display Facebook friend list with login and logout functions. To make the work simpler, we use open source .NET Facebook Developer Toolkit (http://www.codeplex.com/FacebookToolkit) to pull out data from Facebook instead of using Facebook API (JavaScript) for data retrieval. The usage of Facebook Developer Toolkit is simple; just download the facebook.dll and Microsoft.Xml.Schema.Linq.dll from the website, save them to the application bin folder, and add them to the application references.

First we put the Facebook API key and Secret in web.config:
<appSettings>
<add key="facebook.APIKey" value="f81522f787ba594e567ec85424d513b5"/>
<add key="facebook.Secret" value="1426a4ce7cb9be28b6cc927109ef2afa"/>
</appSettings>
We then create an ASP.NET page named FacebookTest.aspx as following:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="FacebookTest.aspx.cs" Inherits="Web.FacebookTest" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
<head id="Head1" runat="server">
<title>Facebook Connect Test</title>
<style type="text/css">
.friendlist li
{
display: inline;
float: left;
margin-left: 5px;
margin-bottom: 5px;
}
</style>
</head>
<body>
<script src=http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php
type="text/javascript"></script>

<form id="form1" runat="server">
<div>
<p><asp:Label ID="lblLoginStatus" runat="server"></asp:Label></p>
<p>
<div runat="server" id="divLogin">
<fb:login-button onlogin="window.location.reload()"></fb:login-button>
</div>

<div runat="server" id="divLogout">
<a href="#" onclick="FB.Connect.logoutAndRedirect('FacebookTest.aspx');">
<img src="http://static.ak.fbcdn.net/images/fbconnect/logout-buttons/logout_small.gif" />
</a>
</div>
</p>
<asp:Label ID="lblFriendCount" runat="server"></asp:Label>
<asp:ListView ID="ListView1" runat="server">
<LayoutTemplate>
<ul class="friendlist">
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li><img src='<%#Eval("imgURL") %>' width="75" height="75" /> <br/> <%#Eval("name") %></li>
</ItemTemplate>
</asp:ListView>
</div>
</form>

<script type="text/javascript">
FB.init("ecf025f742453d4253eb40b074049b7a", "xd_receiver.htm");
</script>
</body>
</html>
The XML namespace
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
is to tell Visual Studio the valid control names from Facebook such as <fb:login-button>. Here we also add a logout button so that user can log out the Facebook session. The Facebook JavaScript and FB initialization (FB.init) are included exactly as the regular html page we tested in previous section. A ListView control is used to display the friends’ image and name.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.Xml.Linq;
using facebook;

namespace Web
{
public partial class FacebookTest : System.Web.UI.Page
{
string FacebookAPIKey = ConfigurationManager.AppSettings["facebook.APIKey"];
string FacebookSecret = ConfigurationManager.AppSettings["facebook.Secret"];

private string GetFacebookCookie(string cookieName)
{
string cookieValue = null;
string fullCookieName = FacebookAPIKey + "_" + cookieName;

if (HttpContext.Current.Request.Cookies[fullCookieName] != null)
{
cookieValue = HttpContext.Current.Request.Cookies[fullCookieName].Value;
}

return cookieValue;
}

protected void Page_Load(object sender, EventArgs e)
{
string sessionKey = GetFacebookCookie("session_key");
int userID = -1;
int.TryParse(GetFacebookCookie("user"), out userID);
bool isConnected = false;
if (int.TryParse(GetFacebookCookie("user"), out userID)
&& userID != -1 && !string.IsNullOrEmpty(sessionKey))
{
isConnected = true;
}
if (isConnected)
{
divLogin.Style["display"] = "none";
divLogout.Style["display"] = "block";

// Setup Facebook Toolkit API
API api = new API();
api.ApplicationKey = FacebookAPIKey;
api.Secret = FacebookSecret;
api.SessionKey = sessionKey;
api.uid = userID;

// Get User Info
facebook.Schema.user user = api.users.getInfo();
string userName = (user == null) ? string.Empty : user.name;

// Get User Friend List
IList<facebook.Schema.user> friends = api.friends.getUserObjects();
List<FBUserInfo> friendList = new List<FBUserInfo>();
foreach (facebook.Schema.user friend in friends)
{
FBUserInfo info = new FBUserInfo()
{
id = friend.uid.HasValue ? friend.uid.Value.ToString() : "",
name = friend.name,
imgURL = friend.pic
};
if (string.IsNullOrEmpty(info.imgURL))
info.imgURL = "http://static.ak.fbcdn.net/pics/q_silhouette.gif";
friendList.Add(info);
}
ListView1.DataSource = friendList;
ListView1.DataBind();

lblLoginStatus.Text = "You have logged in as " + userName;
lblFriendCount.Text = "You have " + friendList.Count.ToString() + " friends.";

}
else // Logged out
{
divLogin.Style["display"] = "block";
divLogout.Style["display"] = "none";
lblLoginStatus.Text = "You have not logged in Facebook.";
}
}

public class FBUserInfo
{
public string id { get; set; }
public string name { get; set; }
public string imgURL { get; set; }
}
}
}
The application first checks the login status by examining the cookie set by Facebook call-back via channel page (xd_receiver.htm). If the cookie is valid, a Facebook Developer Toolkit API object is created and its properties are populated by Facebook Connect setting and user’s login session stored in cookie. We then can use the API object to get user’s name and user’s friend list.

The user’s friends in Facebook will be listed on the page when the user successfully logs in Facebook Connect. Below is the screen-shot of the page:


Filter Friends by Social Network

The friend objects returned by Facebook Connect include a list of affiliations objects that identify the social networks. Filtering friends by affiliation group make the friend selection easier if the user has a large number of friends.

Following code snippet shows how to populate affiliation info in friend list using Facebook Developer toolkit:
// Get User Friend List
IList<facebook.Schema.user> friends = api.friends.getUserObjects();
Dictionary<long, string> affDictionary = new Dictionary<long, string>();
List<FBUserInfo> friendList = new List<FBUserInfo>();
foreach (facebook.Schema.user friend in friends)
{
FBUserInfo info = new FBUserInfo()
{
ID = friend.uid.HasValue ? friend.uid.Value.ToString() : "",
Name = friend.name,
ImageURL = friend.pic
};
if (string.IsNullOrEmpty(info.ImageURL))
info.ImageURL =
"http://static.ak.fbcdn.net/pics/q_silhouette.gif";

// Add social networks
IList<facebook.Schema.affiliation> affList = friend.affiliations.affiliation;
foreach (facebook.Schema.affiliation aff in affList)
{
if (!affDictionary.Keys.Contains(aff.nid))
affDictionary.Add(aff.nid, aff.name);
info.AffiliationIDs += aff.nid.ToString() + ",";
}
friendList.Add(info);
}

ListView1.DataSource = friendList;
ListView1.DataBind();

ddlAffiliation.Visible = friendList.Count > 0;
lblLoginStatus.Text = "You have logged in as " + userName;
lblFriendCount.Text = "You have total " + friendList.Count.ToString() + " friends.";

ddlAffiliation.DataSource = affDictionary;
ddlAffiliation.DataTextField = "Value";
ddlAffiliation.DataValueField = "Key";
ddlAffiliation.DataBind();
ddlAffiliation.Items.Insert(0, new ListItem("All", "0"));
The AffiliationIDs for each friend are stored in a hidden field so the client side JavaScript can set a friend’s visibility based on the selected affiliation ID:
<asp:ListView ID="ListView1" runat="server" EnableViewState="false">
<LayoutTemplate>
<ul class="friendlist">
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li>
<img src='<%#Eval("ImageURL") %>' width="75" height="75" />
<br/> <%#Eval("Name") %>
<input id="hidID" type="hidden" value='<%#Eval("ID") %>'/>
<input id="hidAffIDs" type="hidden" value='<%#Eval("AffiliationIDs") %>'/>
</li>
</ItemTemplate>
</asp:ListView>
jQuery and JavaScript and jQuery update the page when affiliation dropdown is changed. All filtering occurs in client side without any postback and AJAX call:
<script type="text/javascript">
$(document).ready(function() {
$("#<%= ddlAffiliation.ClientID %>").change(affiliationChanged);
});

function affiliationChanged() {
var affiliationID = $("#<%= ddlAffiliation.ClientID %> :selected").val();
var affiliationCount = 0;
$(".friendlist").find("li").each(function(i) {
var ids = $(this).find("#hidAffIDs").val();
$(this).show();
if (affiliationID != "0" && !hasIDInlist(ids, affiliationID))
$(this).hide();
else
affiliationCount++;
});
var affMsg = "There are " + affiliationCount + " friends in " +
$("#<%= ddlAffiliation.ClientID %> :selected").text() + " network.";
$("#<%=lblAffiliationFilter.ClientID %>").html(affMsg);
}

function hasIDInlist(ids, selectedID) {
if (ids) {
var idSplit = ids.split(',');
for (i = 0; i < idSplit.length; i++) {
if (idSplit[i] == selectedID) {
return true;
}
}
}
return false;
}
</script>
When user changes the social network filter, a JavaScript handler will check all friends’ affiliation IDs, and set the friend image invisible if the selected ID is not on the list. Below is screen-shot of the page with affiliation filter:


Sending Message to Selected Friends

For security reason Facebook Connect does not provider email address to other party, and there is no simple way to access user or user’s friends email addresses directly. The detail has been described in http://www.facebook.com/help.php?page=888. However a signed in user can send a Facebook notification to another user (by user’s ID) using Facebook Connect library. In Facebook Developer Toolkit, we can use API’s notifications.send method to send a notification to another user:
api.notifications.send(selectedFriend.id, "This is a message from Facebook Connect Test!");
The message will go to receiver’s Inbox Notification tab:

Some speical characters and html tags inside notification message will be filtered out by Facebook. The allowed tags for email notificaiton are listed in http://wiki.developers.facebook.com/index.php/Allowed_FBML_and_HTML_Tags#Notifications:_Allowed_Tags.

conclusion

With Facebook Connect, we can easily share the Facebook data and use them in our application.

Friday, May 29, 2009

Microsoft Virtual Earth Tutorial

What is Virtual Earth?

Microsoft Virtual Earth (rebranded as Bing Map in middle of 2009) is a platform to visualize location based data over a simple web connection, and is a powerful service that renders interactive maps directly in web pages. By combining the map images provided by Microsoft’s platform with our custom data, we can create an interactive experience for end users to do the location-related searches.

A Simple Map Page

Virtual Earth is rendered purely by JavaScript in client side browser. The latest version of VE JavaScript library is 6.2. Loading a VE Map is simple: reference the VE JavaScript library, create a VEMap object and call loadMap method to show the map. The source of a simple web page and its UI screen-shoot below demos the simplicity of VE Map usage.
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
<script type="text/javascript">
var map = null;
function LoadMap() {
map = new VEMap('myMap');
map.LoadMap();
}
</script>
</head>
<body onload="LoadMap();">
<form id="form1" runat="server">
<div id='myMap' style="position: relative; width: 600px; height: 400px;"></div>
</form>
</body>
</html>
The page looks like:


Map Initial Setting and Show Map Info

VE map initial location and scale can be set in the loading. VELatLong object is passed to LoadMap method to locate the central point of the map, and view level or zoom level can be set from 0 – 19 (default 4 showing the most part United States). To get the map geo info, we use MapView object, and the minimum and maximum latitude/longitude values of the map can be read by map’s corner points. Following page source illustrates how to initialize a map centering at Microsoft with scale of city view, and how to get map information.
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
<script type="text/javascript">
var map = null;
function LoadMap() {
map = new VEMap('myMap');
map.LoadMap(new VELatLong(47.65, -122.14), 12);
showMapInfo();
}
function showMapInfo() {
var view = map.GetMapView();
latMin = view.BottomRightLatLong.Latitude;
latMax = view.TopLeftLatLong.Latitude;
lonMin = view.TopLeftLatLong.Longitude;
lonMax = view.BottomRightLatLong.Longitude;
latCenter = map.GetCenter().Latitude;
lonCenter = map.GetCenter().Longitude;
var mapInfo = 'Map Info - Min lat: '+ latMin.toFixed(6);
mapInfo += ' Max lat:' + latMax.toFixed(6);
mapInfo += ' Min lon: ' + lonMin.toFixed(6);
mapInfo += ' Max lon: ' + lonMax.toFixed(6);
mapInfo += '<br> Center Lat: ' + latCenter.toFixed(6);
mapInfo += ' Center lon: ' + lonCenter.toFixed(6);
mapInfo += ' Zoom Level: ' + map.GetZoomLevel();
document.getElementById('divMapInfo').innerHTML = mapInfo;
}
</script>
</head>
<body onload="LoadMap();">
<form id="form1" runat="server">
<div id="divMapInfo" style="height: 50px;"></div>
<div id='myMap' style="position: relative; width: 600px; height: 400px;"></div>
</form>
</body>
</html>
The page is loaded as:


Capture Map Events

All user interactive actions in the map pages are triggered by events and handled by event handlers. There are more than ten events available on VE maps. To handle a map event, you have to attach it explicitly to an event handler (JavaScript function) by calling map.AttachEvent(eventName, eventHandler). The details of VE Map events are listed in http://msdn.microsoft.com/en-us/library/bb429568.aspx, such as onendzoom event occurring when the map zoom ends and onresize event happens in resizing. Besides the events described in the above link, common mouse-based events, like onclick, ondoubleclick and onmouseover events, can also be attached to a map.

The example map page above only shows the map information of the initial state. To see the updated info when there is a change on the map, we can use the onchangeview event:
       function LoadMap() {
map = new VEMap('myMap');
map.LoadMap(new VELatLong(47.65, -122.14), 12);
map.AttachEvent("onchangeview", showMapInfo);
showMapInfo();
}

Pushpins on the Map

Pushpins are visible locaters on the map. Pushpins can be used to show location specific landmarks or custom data. The pushpin is abstracted in a VE Shape object, and the Shape object model allows users to customize the UI shown on the map. With common DHTML techniques, we can substitute images, controls and dynamic layers for the default pushpin control.

Following code shows how to create a pushpin with image and description:
function LoadMap() {
map = new VEMap('myMap');
map.LoadMap(new VELatLong(47.65, -122.14), 12);
var imageURL = 'http://www.microsoft.com/about/images/link_visitMS_new.gif';
addPushpin(47.65, -122.14, 'My pushpin', 'Just a test', imageURL);
showMapInfo();
}
function addPushpin(lat, lon, title, description, icon) {
var point = new VELatLong(lat, lon);
var shape = new VEShape(VEShapeType.Pushpin, point);
shape.SetTitle(title);
shape.SetDescription(description);
if (icon) {
shape.SetCustomIcon(icon);
}
map.AddShape(shape);
}
function showMapInfo() {
//Skip the rest for abbreviation...
Result:


Showing Dynamic Data Using AJAX and JSON Objects

In this example custom pushpins are populated on the map dynamically using AJAX call with JSON data transfer.jQuery is used to facilitate the AJAX invocation:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Dynamically Loading Virtual Map</title>

<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
<script type="text/javascript" src="http://www.json.org/json2.js"></script>
<script src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js" type="text/javascript"></script>

<script type="text/javascript">
var map = null;
function LoadMap() {
map = new VEMap('myMap');
map.LoadMap(new VELatLong(47.64, -122.13), 12);
map.SetScaleBarDistanceUnit(VEDistanceUnit.Kilometers);
map.AttachEvent("onchangeview", showMapInfo);
showMapInfo();
}

function showMapInfo() {
var view = map.GetMapView();
latMin = view.BottomRightLatLong.Latitude;
latMax = view.TopLeftLatLong.Latitude;
lonMin = view.TopLeftLatLong.Longitude;
lonMax = view.BottomRightLatLong.Longitude;
latCenter = map.GetCenter().Latitude;
lonCenter = map.GetCenter().Longitude;

var mapInfo = 'Map Info - ' + ' Min lat: ' + latMin.toFixed(6);
mapInfo += ' Max lat:' + latMax.toFixed(6);
mapInfo += ' Min lon: ' + lonMin.toFixed(6);
mapInfo += ' Max lon: ' + lonMax.toFixed(6);
mapInfo += '<br> Center Lat: ' + latCenter.toFixed(6);
mapInfo += ' Center lon: ' + lonCenter.toFixed(6);
mapInfo += ' Zoom Level: ' + map.GetZoomLevel();

$('#divMapInfo').html(mapInfo);

if (map.GetZoomLevel() > 15) {
getDataByLatLonArea(latMin, latMax, lonMin, lonMax);
}
else {
map.DeleteAllShapes();
}
}

function getDataByLatLonArea(latMin, latMax, lonMin, lonMax) {
var parameters = "{'latMin':" + latMin + ",'latMax':" + latMax;
parameters += ",'lonMin':" + lonMin + ",'lonMax':" + lonMax + "}";
$.ajax({
type: "POST",
url: "MapService.asmx/GetDataByGeoRange",
data: parameters,
contentType: "application/json; charset=utf-8",
dataType: "json",
error: function(xhr, desc, exceptionobj) {
alert(xhr.responseText);
},
success: function(msg) {
if (msg.d != null) {
var jsonObjects = JSON.parse(msg.d);
//var mapInfo = $('#divMapInfo').html();
//mapInfo += " <b>Item found:" + jsonObjects.length; $('#divMapInfo').html(mapInfo);
$.each(jsonObjects, function(i, item) {
var description = populateDescription(item);
addPushpin(item.Latitude, item.Longitude, item.Name, description, null);
});
}
}
});
}

function populateDescription(item) {
var description = "<br><b>Description for " + item.Name + "<br><br>";
return description;
}

function addPushpin(lat, lon, title, description, icon) {
var point = new VELatLong(lat, lon);
var shape = new VEShape(VEShapeType.Pushpin, point);
shape.SetTitle(title);
shape.SetDescription(description);
if (icon) {
shape.SetCustomIcon(icon);
}
map.AddShape(shape);
}
</script>

</head>
<body onload="LoadMap();">
<form id="form1" runat="server">
<div id="divMapInfo" style="height: 50px;">
</div>
<div id='myMap' style="position: relative; width: 600px; height: 400px;">
</div>
</form>
</body>
</html>
Note that we only show the pushin data when the zoom level is greater than 15, and won’t add any pushpins on the map when the scale of the map is too big.

The Web Service returns data in JSON format, which is generated by DataContractJsonSerializer class directly, a new feature in .NET 3.5:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Services;
using System.Text;
using System.IO;

//From System.Web.Extensions assembly (.NET 3.5 SP1)
using System.Web.Script.Services;
//From System.ServiceModel.Web assembly (.NET 3.5 SP1)
using System.Runtime.Serialization.Json;

namespace Web
{
/// <summary>
/// Summary description for MapService
/// </summary>
[ScriptService]
[WebService(Namespace = "http://tempuri.org/")]
public class MapService : System.Web.Services.WebService
{
[WebMethod]
public string GetDataByGeoRange(double latMin, double latMax, double lonMin, double lonMax)
{
List<MapData> dataList = MapData.GetDataByGeoRange(latMin, latMax, lonMin, lonMax);
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(dataList.GetType());
StringBuilder sb = new StringBuilder();
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, dataList);
sb.Append(Encoding.UTF8.GetString(ms.ToArray()));
}
return sb.ToString();
}
}

public class MapData
{
private struct Point
{
public string Name;
public double Latitude;
public double Longitude;
public Point(string name, double lat, double lon)
{
Name = name;
Latitude = lat;
Longitude = lon;
}
}

public string Name { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }

static private List<Point> mockData = GetMockData();
static private List<Point> GetMockData()
{
int pointNumber = 1000;
double latMin = 47.593662, latMax = 47.686192;
double lonMin = -122.232857, lonMax = -122.026863;
List<Point> mockPoints = new List<Point>();
Random rand = new Random();
for (int i = 0; i < pointNumber; i++)
{
double lat = rand.NextDouble() * (latMax - latMin) + latMin;
double lon = rand.NextDouble() * (lonMax - lonMin) + lonMin;
Point point = new Point("Mock " + i.ToString(), lat, lon);
mockPoints.Add(point);
}
return mockPoints;
}
public static List<MapData> GetDataByGeoRange(double latMin, double latMax, double lonMin, double lonMax)
{
List<MapData> dataList = new List<MapData>();
foreach (Point point in mockData)
{
if (point.Latitude > latMin && point.Latitude < latMax &&
point.Longitude > lonMin && point.Longitude < lonMax)
{
MapData mapData = new MapData();
mapData.Latitude = string.Format("{0:0.000000}", point.Latitude);
mapData.Longitude = string.Format("{0:0.000000}", point.Longitude);
mapData.Name = point.Name;
dataList.Add(mapData);
}
}
return dataList;
}
}
}
The screen shot of the map with dynamically loaded data:


Conclusion

In this article we examine a few simple scenarios of using Microsoft Virtual Earth. In reality we can build very powerful location-based web applications using Microsoft Virtual Earth as a platform.

Tuesday, May 12, 2009

SharePoint CKS - User Group Edition 1.0 Solution Package

The Toronto SharePoint User Group website (http://www.tspug.com/) needs to rebuild for some considerations. Bill Brockbank, a SharePoint MVP, has been preparing on this for a while. He suggested to use Community Kit for SharePoint (CKS) as template to start our work. What we are using is the User Group Edition, a SharePoint stp template. There's some limitation in stp template in terms of development and deployment. So we decided to convert the stp to a solution package with Site Definition.

Creating a SharePoint Site Definition from scratch is not a trivial task for me. I spent quite a bit of time writing all those XML files including the ONET.XML. It was a good exercise and I got more understanding of what's under the hood of building a new SharePoint site. With the solution conversion completed, our new user group website will be setup online soon.

The whole solution package can be download here.

Thursday, April 30, 2009

SQL Server 2008 Spatial Data And Spatial Search

SQL Server 2008 offers a great support for spatial data and spatial search. Spatial data, or geo-spatial data, means locations on the Earth. Microsoft introduces two new data types in SQL Server 2008 to present location data:

Geometry: Geometry data are based on a flat Earth model, and it doesn’t account for the Earth’s curvature.

Geography: Geography data are Points, lines, polygons, or collection of these in latitude and longitude coordinates using a round Earth model.

What’s good about SQL Server 2008 spatial data? The location information can be stored as native Geography data and indexed, so that the location-based search can be efficiently done by SQL Server 2008. I examined such spatial search on a test database with 1-million rows of test data. The result is quite impressive.

Red Gate SQL Data Generator (V1.028) is used to create 1-million test data. Following script is to mock valid latitude and longitude data inside U.S.:

-- UDF Can't use RAND directly. Using View to cheat it
CREATE VIEW vRandNumber AS SELECT RAND() as RandNumber
GO
-- UDF function returns a float number in a given range
CREATE FUNCTION RandFloat(@Min float, @Max float)
RETURNS float AS
BEGIN
RETURN @Min + (select RandNumber from vRandNumber) * (@Max-@Min)
END
GO
-- Update Address with U.S. geo codes
UPDATE Address
SET Address.Latitude = dbo.RandFloat(32, 48),
Address.Longitude = dbo.RandFloat(-124, -72)

With valid Latitude and Longitude data, now we can create a Gepgraphy column in the database:
ALTER TABLE Address ADD GeoPoint Geography NULL
GO
UPDATE Address SET GeoPoint = geography::Point(Latitude, Longitude, 4326)
GO
Querying on this un-indexed Geopgraphy column is very slow. Search locations within a 10-KM radius takes more than 5 minutes:
SET @point = geography::Point(47.32, -122.18, 4326) -—Seattle
SELECT * FROM Address WHERE GeoPoint.STDistance(@point) < 10 * 1000
Result: return 85 rows in 5 minutes 48 seconds

Once the Spatial index is created, the search is much faster than before and the same search only takes 1 second:
CREATE SPATIAL INDEX SIX_Address_GeoGraphy ON Address(GeoPoint);
GO
SELECT * FROM Address WHERE GeoPoint.STDistance(@point) < 10 * 1000
GO
Return 85 rows in 1 second
The test was conducted inside a virtual machine with Intel 2.2G Core2Duo CPU and 2G of RAM.

Monday, February 16, 2009

Cross Domain jQuery AJAX Call

In AJAX and jQuery, I talked about how to use jQuery and AJAX to dynamically get data from a separate page. But that example has a limitation: the AJAX call has to be in the same domain of initial page, otherwise the AJAX requests are characterized as Cross-domain Request ("XDR"), and all popular web browsers will deny those cross domain requests due to security consideration. For example, a JavaScript (AJAX call) in a page inside domain abc.com won't be able to talk with bcd.com because abc.com and bcd.com are in different domains.

However there're some workarounds for such issue. jQuery uses the JSONP approach. Following example shows how to dynamically populate weather information from a different domain. The JavaScript and the page content is below:
<script language="javascript">
/* Sample of JSON Return:
({ "Location":{"Code":"CityCode_1141","Name":"HFLaJlrf"},
"WeatherInfoList":[
{"Day":"Mon","Image":"Images\/5.gif","Temperature":"32°\/22°"},
{"Day":"Tue","Image":"Images\/28.gif","Temperature":"20°\/-8°"},
{"Day":"Wed","Image":"Images\/9.gif","Temperature":"7°\/13°"},
{"Day":"Thu","Image":"Images\/5.gif","Temperature":"11°\/-6°"}
]})
*/

/* Dynamically load weather info using cross domain Ajax call with JSON */
/* Refer to: http://docs.jquery.com/Release:jQuery_1.2/Ajax#Cross-Domain_getJSON_.28using_JSONP.29
http://www.theurer.cc/blog/2005/12/15/web-services-json-dump-your-proxy/
*/

function getWeatherJSON() {
var server = "http://myserver/WeatherService.aspx"; // XDR request
var weatherServiceUrl = server + "?location=" + weatherLocation + "&format=json&jsoncallback=?";
$.getJSON(weatherServiceUrl, function(data) {
$("#WeatherTitle").html(data.Location.Name + " Code(" + data.Location.Code + ")");
/* Loop through all items and set weather data*/
$.each(data.WeatherInfoList, function(i, item) {
$("#Weather_Day" + i + "_Day").html(item.Day);
$("#Weather_Day" + i + "_Image").attr("src", item.Image);
$("#Weather_Day" + i + "_Temperature").html(item.Temperature);
});
$("#divWeatherControl").show();
$("#divWeatherLoading").hide();
});
}
</script>

<div id="divWeatherControl" style="border: solid 1px blue; text-align: center; display: none;">
<table>
<tr align="center">
<td colspan="4"><p id="WeatherTitle" /></td>
</tr>
<tr align="center">
<td><p id="Weather_Day0_Day" /></td>
<td><p id="Weather_Day1_Day" /></td>
<td><p id="Weather_Day2_Day" /></td>
<td><p id="Weather_Day3_Day" /></td>
</tr>
<tr align="center">
<td><img id="Weather_Day0_Image" /></td>
<td><img id="Weather_Day1_Image" /></td>
<td><img id="Weather_Day2_Image" /></td>
<td><img id="Weather_Day3_Image" /></td>
</tr>
<tr align="center">
<td><p id="Weather_Day0_Temperature" /></td>
<td><p id="Weather_Day1_Temperature" /></td>
<td><p id="Weather_Day2_Temperature" /></td>
<td><p id="Weather_Day3_Temperature" /></td>
</tr>
</table>
</div>
<div id="divWeatherLoading" style="border: solid 1px blue; text-align: center;">
Loading weather data...
</div>
Note myserver/weatherservice.aspx can be any domain. The code behind of the weatherservice.aspx page (empty html):
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;

public partial class WeatherService : System.Web.UI.Page
{
private readonly string WEATHERLOCACTIONNAME = "location";
private readonly string CALLBACKFUNCNAME = "jsoncallback";

protected void Page_Load(object sender, EventArgs e)
{
string html = RenderJSON();
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Write(html);
Response.End();
}

private string RenderJSON()
{
string location = Request.QueryString[WEATHERLOCACTIONNAME];
string callBackFunction = Request.QueryString[CALLBACKFUNCNAME];
if (!string.IsNullOrEmpty(location) && !string.IsNullOrEmpty(callBackFunction))
{
WeatherInfo wloc = BLL.GetWeatherForecast(location);
if (wloc == null) return string.Empty;
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(wloc.GetType());
StringBuilder sb = new StringBuilder();
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, wloc);
sb.Append(callBackFunction + "(");
sb.Append(Encoding.UTF8.GetString(ms.ToArray()));
sb.Append(")");
}
return sb.ToString();
}
return string.Empty;
}
}
The result looks like:

Friday, January 09, 2009

AJAX And jQuery

AJAX (Asynchronous JavaScript and XML) has become a powerful platform for building web applications with extensive client-side interactivity. Unlike older approaches, which require reloading of the entire page with every postback, AJAX uses the JavaScript DOM, the XMLHttpRequest object, XML, and CSS to download and display just the content that needs to change. ASP.NET AJAX library, part of ASP.NET 3.5, wraps up the related AJAX features into a set of JavaScript functions so that developers can easily do tasks like partially page update.

ASP.NET AJAX is great, but more and more ASP.NET AJAX developers move to jQuery. Comparing to ASP.NET AJAX, jQuery is more flexible, efficient and platform independent.

What’s jQuery anyway? jQuery is a lightweight yet powerful JavaScript library that simplifies various task for developers like HTML document traversing, event handling, animating, and Ajax interactions for rapid web development.

jQuery is written by JavaScript and all old JavaScripts are still working fine on jQuery framework. jQuery provides its standard jQuery object under the dollar symbol $. jQuery uses CSS style selectors which makes manipulations on page DOM objects very easy. Following example shows how to dynamically filter images based on the photo’s categories:
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#ddlCategory").change(ddlCategoryChanged);
});
function ddlCategoryChanged() {
var categoryID = $("#ddlCategory :selected").val();
$(".photo").find("li").each(function(i) {
var ids = $(this).find("input:hidden").val();
$(this).show();
if (categoryID != "0" && !hasIDInlist(ids, categoryID)){
$(this).hide();
}
});
}
function hasIDInlist(ids, selectedID) {
if (ids) {
var idSplit = ids.split(',');
for (i = 0; i < idSplit.length; i++) {
if (idSplit[i] == selectedID) {
return true;
}
}
}
return false;
}
</script>
</head>
<body>
Filter photos by category:
<select name="ddlCategory" id="ddlCategory">
<option value="0">All</option>
<option value="1">Outdoor</option>
<option value="2">Indoor</option>
<option value="3">Family</option>
<option value="4">Friends</option>
<option value="5">Colleagues</option>
</select>
<ul class="photo">
<li><img src='photo1.gif' /><br />Photo 1
<input type="hidden" value='1,5'/></li>
<li><img src='photo2.gif' /><br />Photo 2
<input type="hidden" value='2,3'/></li>
<li><img src='photo3.gif' /><br />Photo 3
<input type="hidden" value='1,4'/></li>
<li><img src='photo4.gif' /><br />Photo 4
<input type="hidden" value='1,3'/></li>
<li><img src='photo5.gif' /><br />Photo 5
<input type="hidden" value='2,3'/></li>
<li><img src='photo6.gif' /><br />Photo 6
<input type="hidden" value='5'/></li>
<li><img src='photo7.gif' /><br />Photo 7
<input type="hidden" value='4'/></li>
<li><img src='photo8.gif' /><br />Photo 8
<input type="hidden" value='1,4'/></li>
</ul>
</body>
</html>
Each photo’s category Ids are stored in a hidden field. Instead of putting event handler inside each control like onclick=’javascriptFunction’, the selection dropdown event handler is registered in an unobtrusive way ($("#ddlCategory").change(ddlCategoryChanged);) When event is fired, it will go through all photo containers by selecting <li> HTML element under CSS class of photo; the category Ids are retrieved and are compared with the selected category ID; the photo container’s visibility is set based on the category ID comparison result. All these are accomplished by just a few lines of jQuery code:
    $(".photo").find("li").each(function(i) {
var ids = $(this).find("input:hidden").val();
$(this).show();
if (categoryID != "0" && !hasIDInlist(ids, categoryID)){
$(this).hide();
}});
jQuery has great AJAX support, and the AJAX invocation in jQurey is super concise (could be only one line of code). jQuery version 1.3.2 offers six AJAX function calls:

jQuery.ajax(options): Load a remote page using an HTTP request; the most customizable AJAX function in jQuery.
jQuery.load(url, data, callback): Load HTML from a remote file and inject it into the DOM.
jQuery.get(url, data, callback, type): Load a remote page using an HTTP GET request.
jQuery.getJSON( url, data, callback ): Load JSON data using an HTTP GET request; supports Cross-Domain call.
jQuery.getScript( url, callback ): Loads, and executes, a local JavaScript file using an HTTP GET request.

It's quite simple to use those jQuery AJAX functions. Following example shows how to get current time from a jQuery call. Suppose we have a simple page to display current time (Time.aspx):
<%@ page language="C#" %>
<html>
<head>
<title>Simply show current time</title>
<script runat="server">
void Page_Load(object sender, System.EventArgs e)
{
lblTime.Text = DateTime.Now.ToString();
}
</script>
</head>
<body>
<asp:Label ID="lblTime" runat="server"></asp:Label>
</body>
</html>
Then we can get current time from another page using jQuery ajax call:
<html>
<head>
<title></title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#btnGetTime").click(btnClick);
});
function btnClick() {
var searchUrl =
"Time.aspx";
$.ajax({
url: searchUrl,
success: function(data) {
//alert($(data).filter(
"#lblTime").html());
$(
"#divResult").html(data);
}
});
}
</script>
</head>
<body>
<input type="button" value="Get Current Time" id="btnGetTime">
<div id="divResult">
</div>
</body>
</html>
The clean html is another advantage of using jQuery. No postback, no view states are required or generated by jQuery when we use jQuery inside ASP.NET.

Friday, December 19, 2008

Dynamic Javascript Function Invocation

Such dynamic JavaScript call may become handy in some scenario:
    function functionCall(data, option) {
        if (someCondition) {
            var dynamicFunc = function1;
            var args = [data, option, 1];
        } else {
            var dynamicFunc = function2;
            var args = [data, option, 2];
        }
        var value = dynamicFunc.apply(dynamicFunc, args);
        // processing value 
    }

Sunday, December 07, 2008

Design Patterns By Example: Strategy vs Template vs Builder vs Bridge

In this post I will go through a few related design patterns. Unlike many online design pattern tutorials, patterns concept and UML diagrams will not be our focus. Instead I will provide more understandable code examples for the discussion.

Strategy Pattern

Strategy defines an interface common to all supported algorithms or behavior. The implementation of the Strategy is dynamically invoked at run time. Generic list sorting in .NET is a good example of using strategy pattern:

using System;
using System.Collections.Generic;

class test {

    class Person
    {
        public string name { get; set; }
        public int Age { get; set; }
    }

    class PersonSortByAge : IComparer<Person>
    {
        public int Compare(Person p1, Person p2)
        {
            return p1.Age.CompareTo(p2.Age);
        }
    }

    static void Main(string[] args)
    {
        List<Person> people = new List<Person>();
        people.Add(new Person() { name = "Andy Wade", Age = 20 });
        people.Add(new Person() { name = "Cary Lee", Age = 10 });
        people.Add(new Person() { name = "Bob Ford", Age = 30 });

        Console.WriteLine("Original order:");
        people.ForEach(p => Console.WriteLine("Name:" + p.name + " Age:" + p.Age));

        people.Sort((p1, p2) => p1.name.CompareTo(p2.name));
        Console.WriteLine("Sort by name:");
        people.ForEach(p => Console.WriteLine("Name:" + p.name + " Age:" + p.Age));
        //Sort by name:
        //Name:Andy Wade Age:20
        //Name:Bob Ford Age:30
        //Name:Cary Lee Age:10

        people.Sort(new PersonSortByAge());
        Console.WriteLine("Sort by age:");
        people.ForEach(p => Console.WriteLine("Name:" + p.name + " Age:" + p.Age));
        //Sort by age:
        //Name:Cary Lee Age:10
        //Name:Andy Wade Age:20
        //Name:Bob Ford Age:30

        Console.ReadKey();
    }
}

In above example Lambda express and strongly typed IComparer are two different strategy or sorting algorithm, which are used to sort the people list by person's name and age respectively. Following code is another example:

using System;
using System.Collections.Generic;
using System.Linq;

class StrategyPattern
{
    enum Temperature { Cold, Mild, Hot }

    interface IActivity
    {
        string GetActivity(Temperature temperature);
    }

    class DayPlanner
    {
        private Temperature Weather;

        public DayPlanner(Temperature weather)
        {
            this.Weather = weather;
        }

        public void SetPlan(IActivity activity)
        {
            Console.WriteLine("Setting plan for " + activity.GetActivity(Weather));
        }
    }

    class IndoorActivity : IActivity
    {
        public string GetActivity(Temperature temperature)
        {
            return (temperature == Temperature.Cold) ? "Hockey" : "Fitness gym";
        }
    }

    class OutdoorActivity : IActivity
    {
        public string GetActivity(Temperature temperature)
        {
            switch (temperature)
            {
                case Temperature.Cold:
                    return "Ice skating";
                case Temperature.Mild:
                    return "Hiking";
                case Temperature.Hot:
                default:
                    return "Swimmig";
            }
        }
    }

    static void Main(string[] args)
    {
        DayPlanner planner = new DayPlanner(Temperature.Mild);
        
        OutdoorActivity outdoorActivity = new OutdoorActivity();
        planner.SetPlan(outdoorActivity); 
        // print out "Setting plan for Hiking"

        IndoorActivity indoorActivity = new IndoorActivity();
        planner.SetPlan(indoorActivity);  
        // print out "Setting plan for Fitness gym"

        Console.ReadKey();
    }
}

Here the DayPlanner holds a reference of an IActivity instance, an implementation of a strategy. With different strategy assigned, DayPlanner shows different activity. Strategy pattern results in dynamic behavior.

Template Method Pattern

Template method defines a sequence of method calls or some algorithms in base class, and the detail implementation of each method or algorithm is overriden by subclass. One obvious example is the ASP.NET Page life cycle: PreLoad, OnLoad, PreRender, etc. Those events are invoked by base Page class and developers are responsible to fill the detail implementation in code-behind for those events. Following code illustrate how the PersonDayPlanner schedules morning, afternoon and night activities by template method:

using System;
using System.Collections.Generic;

class TemplateMethodPattern
{
    abstract class PersonDayPlanner
    {
        // Template Method defines the skeleton steps
        public void Plan()
        {
            MorningPlan();
            AfternoonPlan();
            NightPlan();
        }

        // Abstract methods implemented by child classes
        public abstract void MorningPlan();
        public abstract void AfternoonPlan();
        public abstract void NightPlan();
    }

    class CollegeStudentSaturday : PersonDayPlanner
    {
        public override void MorningPlan()
        {
            Console.WriteLine("Have a simple breakfast and do cleanup.");
        }

        public override void AfternoonPlan()
        {
            Console.WriteLine("Study for 2 hours then go to gym for exercise.");
        }

        public override void NightPlan()
        {
            Console.WriteLine("Eat out with friends and watch movie after");
        }
    }

    class LittleKidSaturday : PersonDayPlanner
    {
        public override void MorningPlan()
        {
            Console.WriteLine("McDonald's happy meal for breakfast, then play at park.");
        }

        public override void AfternoonPlan()
        {
            Console.WriteLine("Friend's birthday party.");
        }

        public override void NightPlan()
        {
            Console.WriteLine("Play LEGO and watch TV.");
        }
    }

    static void Main(string[] args)
    {
        PersonDayPlanner saturday = new CollegeStudentSaturday();
        saturday.Plan();
        Console.WriteLine();
        //print out a student's plan on Saturday:
        // Have a simple breakfast and do cleanup.
        // Study for 2 hours then go to gym for exercise.
        // Eat out with friends and watch movie after

        saturday = new LittleKidSaturday();
        saturday.Plan();
        //print out a kid's plan on Saturday:
        // McDonald's happy meal for breakfast, then play at park.
        // Friend's birthday party.
        // Play LEGO and watch TV.

        Console.ReadKey();
    }
}

Builder Pattern

The Builder pattern separates the construction logic for an object. This pattern is often used when the construction process of an object is complex. By modifying above DayPlanner code demo we have following builder implementation:

using System;
using System.Collections.Generic;

class BuilderPattern
{
    interface IPersonDayPlan
    {
        void MorningPlan();
        void AfternoonPlan();
        void NightPlan();
    }

    class DayPlanBuilder
    {
        private IPersonDayPlan DayPlan { get; set; }

        public DayPlanBuilder() { } // default constructor

        public DayPlanBuilder(IPersonDayPlan dayPlan)
        {
            this.DayPlan = dayPlan;
        }

        public void SetPersonDayPlan(IPersonDayPlan dayPlan)
        {
            this.DayPlan = dayPlan;
        }

        public void BuildPlan()
        {
            if (this.DayPlan != null)
            {
                this.DayPlan.MorningPlan();
                this.DayPlan.AfternoonPlan();
                this.DayPlan.NightPlan();

                // You can control the steps or add new logic inside the builder
                if (this.DayPlan is CollegeStudentSaturday)
                {
                    Console.WriteLine("Play games no later than 12PM.");
                }
            }
        }
    }

    class CollegeStudentSaturday : IPersonDayPlan
    {
        public void MorningPlan()
        {
            Console.WriteLine("Have a simple breakfast and do cleanup.");
        }

        public void AfternoonPlan()
        {
            Console.WriteLine("Study for 2 hours then go to gym for exercise.");
        }

        public void NightPlan()
        {
            Console.WriteLine("Eat out with friends and watch movie after");
        }
    }

    class LittleKidSaturday : IPersonDayPlan
    {
        public void MorningPlan()
        {
            Console.WriteLine("McDonald's happy meal for breakfast, then play at park.");
        }

        public void AfternoonPlan()
        {
            Console.WriteLine("Friend's birthday party.");
        }

        public void NightPlan()
        {
            Console.WriteLine("Play LEGO and watch TV.");
        }
    }

    static void Main(string[] args)
    {
        DayPlanBuilder planBuilder = new DayPlanBuilder(new LittleKidSaturday());
        planBuilder.BuildPlan();
        //print out:
        // McDonald's happy meal for breakfast, then play at park.
        // Friend's birthday party.
        // Play LEGO and watch TV.

        // changing to student's plan
        planBuilder.SetPersonDayPlan(new CollegeStudentSaturday());
        planBuilder.BuildPlan();
        //print out:
        // Have a simple breakfast and do cleanup.
        // Study for 2 hours then go to gym for exercise.
        // Eat out with friends and watch movie after
        // Play games no later than 12PM.

        Console.ReadKey();
    }
}

The Builder pattern plays a little bit different "director" role compared with the Template method in the build process. In above example BuildPlan method can have different build steps for different IPersonDayPlan types; while the Template method has fixed run steps. Builder is creational pattern and it is mainly used to create complex object. Template method is behavior pattern and it defines skeleton of running procedure.

Bridge Pattern

Both Template method and Builder pattern use inheritance to implement different behaviors or algorithms. The situation would become messy when more and more objects join and play in the hierarchy. In above DayPlanner example, we could have AdultMonday, AdultTuesday...SeniorMonday, SeniorTuesday...etc. of many other combination for sub classes. Bridge pattern resolves the problem by decoupling an abstraction from its implementation so that the two can vary independently. Modify the code example again the Bridge version looks like:

using System;
using System.Collections.Generic;

class BridgePattern
{
    interface IDayPlanner
    {
        string DayOfWeek { get; }
        void Plan(int age, bool inSchool);
    }

    abstract class Person
    {
        public int Age { get; set; }
        protected IDayPlanner DayPlanner { get; set; }

        public void SetDayPlanner(IDayPlanner dayPlanner)
        {
            this.DayPlanner = dayPlanner;
        }

        public abstract void PlanDay();
    }

    class CollegeStudent : Person
    {
        public CollegeStudent(int age)
        {
            base.Age = age;
        }

        public override void PlanDay()
        {
            Console.WriteLine("College student's " + this.DayPlanner.DayOfWeek + " schedule:");
            this.DayPlanner.Plan(this.Age, true);
        }
    }

    class LittleKid : Person
    {
        public LittleKid(int age)
        {
            base.Age = age;
        }

        public override void PlanDay()
        {
            Console.WriteLine("Little kid's " + this.DayPlanner.DayOfWeek + " schedule:");
            this.DayPlanner.Plan(this.Age, false);
        }
    }

    class SaturdayPlanner : IDayPlanner
    {
        public void Plan(int age, bool inSchool)
        {
            if (age < 6)
            {
                Console.WriteLine("Play at park, friend's birthday party, and play LEGO.");
            }
            else if (age > 18 && inSchool)
            {
                Console.WriteLine("Study, gym exercise and social activities.");
            }
            // plan for other cases
        }

        public string DayOfWeek { get { return "Saturday"; } }
    }

    class MondayPlanner : IDayPlanner
    {
        public void Plan(int age, bool inSchool)
        {
            if (age < 6)
            {
                Console.WriteLine("Kindergarten.");
            }
            else if (age > 18 && inSchool)
            {
                Console.WriteLine("Classes, library and lab.");
            }
            // plan for other cases
        }

        public string DayOfWeek { get { return "Monday"; } }
    }

    static void Main(string[] args)
    {
        Person kid = new LittleKid(5);
        kid.SetDayPlanner(new SaturdayPlanner());
        kid.PlanDay();
        //Little kid's Saturday schedule:
        //Play at park, friend's birthday party, and play LEGO.

        Person student = new CollegeStudent(20);
        student.SetDayPlanner(new MondayPlanner());
        student.PlanDay();
        //College student's Monday schedule:
        //Classes, library and lab.

        Console.ReadKey();
    }
}

Now person and weekday are separated, and we don't have to do their combinational sub-classes anymore, known as "prefer composition over inheritance" for such scenario. The Provider model in .NET framework, such as Membership Provider and Session State Provider, is an example of using Bridge pattern.

Last but not least, design patterns are useful to resolve certain problems but they are not a silver bullet. Overusing them could introduce unnecessary complexity and cost. We should't force to use patterns as what I do in this posting. The pattern examples I presented above are just for demonstration purpose and it doesn't mean they are the best practices. In reality we should consider those patterns when having a code smell in our work.