Wednesday, April 8, 2009

EQATEC .NET App Profiler

Someone posted a link to this .NET Profiler tool on Twitter (Twitter FTW!) - so I went and downloaded it and tried it. The tool is called EQATEC Profiler (you can download it here). So what is it?

The way it works it that it rebuild your dlls/exes so when you run it, the tool will be able to profile your .NET method calls and give you a report. Based on the report, you can see which method is the slowest, which one is being called the most number of times, etc. The hope is that based on that data then you can optimize your application to maximize the speed/performance.

When you downloaded the app, it comes with a sample WinForm app. You can use it to see how it works. When it finds the exe/Main() method, it will use it as a starting point when you run it.

My application is an ASP.NET MVC application - so it does not have a Main() method. How do I then profile it?

When you open the profiler, this the screen you will see. Click browse on the top right - and browse to your ASP.NET application bin directory. You can select a dll OR not select any file and just the folder (which will select all dlls in the bin folder). Hit "OK" then make sure all the dlls that you want to profile is checked. Hit the "Build" button on the lower right. Once it's done, you will see that there is a message in the message box saying that it does not find a Main() method. This is OK. Click "Open output folder" on the lower left, and copy all dlls in there back into your original bin folder. Then run your web application (Do NOT run it by hitting F5 - which will overwrite all the dlls from EQATEC. But run it by opening a browser and type "http://localhost:<port>/<appname>". At this point the EQATEC is already attached to your ASP.NET web app process and ready to profile it. So run through your web app (like doing a comprehensive walk-through) and before you are done, click "Run" tab on the EQATEC profiler then hit the "Take snapshot" button. If you are done at this point, you can close your browser. You can take a snapshot anytime you want and they are accumulative - so if you took 5 snapshots, the latest one will have the most comprehensive data out of the 5.

Now highlight the top most (or latest) snapshot you have taken, then click "View" button on the lower right of the "Run" tab. It displays the break-down of all of your method calls during the walk-through. The telemetry includes:

  • "Calls" - which is the number of times the method is being called during the walk-through session
  • "Avg (self)" - which is the average amount of time spent in that method alone in milliseconds.
  • "Total (self)" is the total amount of time spent in the method during the whole time spent in the walk-through.
  • "Total (full)" - the total amount of time spent in the method and all the other methods called by it.
  • "Avg (full)" is the average - "Total (full) / Calls".

From those data, we should be able to see what method takes the longest to execute and optimize it if necessary - OR - why is a particular method is being called hundred of times and accumulate cost over time and maybe we can put caching to reduce it, etc etc.

Overall, I like this tool as it provides me a way to pin-point the bottlenecks in my application and take action accordingly, instead of just guessing or doing a manual measurement - which is painful and tedious.
-- read more and comment ...

Monday, April 6, 2009

Silverlight 3.0, Astoria, LINQ to SQL (3): DataForm CRUD

In last few posts:here and here, I wrote about how to build/prepare the solution, projects, and some ground work in LINQ to SQL and connecting it to ADO.NET Data Service as well as reading data through a Silverlight application using Silverlight 3.0 DataForm.

In this post, I will continue in implementing the Update/Edit, Create/New, and Delete to complete our CRUD.

To do EDIT, it is actually very simple. First you will need to add event handlers. We will need two handlers, but both of them will be reusable for ADD. To add event handlers, all we need to do is start typing our DataForm name and select the event that we want to implement via intellisense and hit tab key twice. It will complete the line for you and create a stub for the event hendler method.

By the way, I changed our binding, so instead of going against Customer, it is against Shipper (both from Northwind db and should exist in the LINQ mapping from the auto-gen code).

static Uri baseUri = new Uri("http://localhost:10403/NorthwindDataService.svc", UriKind.Absolute);
NorthwindDBDataContext northwindDSContext = new NorthwindDBDataContext(baseUri);

public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);

// new code
dfShipper.ItemEditEnding += new EventHandler<DataFormItemEditEndingEventArgs>(dfShipper_ItemEditEnding);
dfShipper.ItemEditEnded += new EventHandler<DataFormItemEditEndedEventArgs>(dfShipper_ItemEditEnded);
}

void dfShipper_ItemEditEnding(object sender, DataFormItemEditEndingEventArgs e)
{
northwindDSContext.UpdateObject(dfShipper.CurrentItem);
}

void dfShipper_ItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
{
northwindDSContext.BeginSaveChanges((asyncResult) =>
{ northwindDSContext.EndSaveChanges(asyncResult); }, null);
}

The "BeginSaveChanges" and "EndSaveChanges" are there because our data connection is asynchronous.

For ADD, we need to change "dfShipper_ItemEditEnding" into this:
void dfShipper_ItemEditEnding(object sender, DataFormItemEditEndingEventArgs e)
{
if (dfShipper.IsAddingNew)
northwindDSContext.AddObject("Shippers", dfShipper.CurrentItem);
else
northwindDSContext.UpdateObject(dfShipper.CurrentItem);
}

For DELETE, we add this handler:
void dfShipper_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
{
northwindDSContext.DeleteObject(dfShipper.CurrentItem);
northwindDSContext.BeginSaveChanges((asyncResult) =>
{ northwindDSContext.EndSaveChanges(asyncResult); }, null);
}

So our complete code should look like this:
public partial class MainPage : UserControl
{
static Uri baseUri = new Uri("http://localhost:10403/NorthwindDataService.svc", UriKind.Absolute);
NorthwindDBDataContext northwindDSContext = new NorthwindDBDataContext(baseUri);

public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
dfShipper.ItemEditEnding += new EventHandler<DataFormItemEditEndingEventArgs>(dfShipper_ItemEditEnding);
dfShipper.ItemEditEnded += new EventHandler<DataFormItemEditEndedEventArgs>(dfShipper_ItemEditEnded);
dfShipper.DeletingItem += new EventHandler<System.ComponentModel.CancelEventArgs>(dfShipper_DeletingItem);
}

void dfShipper_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
{
northwindDSContext.DeleteObject(dfShipper.CurrentItem);
northwindDSContext.BeginSaveChanges((asyncResult) =>
{ northwindDSContext.EndSaveChanges(asyncResult); }, null);
}

void dfShipper_ItemEditEnding(object sender, DataFormItemEditEndingEventArgs e)
{
if (dfShipper.IsAddingNew)
northwindDSContext.AddObject("Shippers", dfShipper.CurrentItem);
else
northwindDSContext.UpdateObject(dfShipper.CurrentItem);
}

void dfShipper_ItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
{
northwindDSContext.BeginSaveChanges((asyncResult) =>
{ northwindDSContext.EndSaveChanges(asyncResult); }, null);
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var query = (DataServiceQuery<Shipper>)
from c in northwindDSContext.Shippers
orderby c.CompanyName
select c;

query.BeginExecute((asyncResult) =>
{
try
{
IEnumerable<Shipper> result = query.EndExecute(asyncResult);
Dispatcher.BeginInvoke(() =>
{
ObservableCollection<Shipper> data = new ObservableCollection<Shipper>();
foreach (Shipper p in result)
{
data.Add(p);
}
dfShipper.DataContext = data;
dfShipper.ItemsSource = data;
});
}
catch (Exception ex)
{
throw ex;
}
}, null);
}
}

-- read more and comment ...

Wednesday, April 1, 2009

Silverlight 3.0, Astoria, LINQ to SQL (2): DataForm CRUD

In the last post, I wrote about how to build/prepare the solution, projects, and some ground work in LINQ to SQL and connecting it to ADO.NET Data Service. Now in this post, I am going to continue in building the Silverlight application using Silverlight 3.0 DataForm.

We will first add the service reference to our ADO.NET Service that we built from our Silverlight application. Then how to hook it up to our DataForm so it will display the first record by default.

1. Add a service reference to the ADO.NET Data Service ("NorthwindDataService.svc"). Run the solution with "NorthwindDataService.svc" as the Start page. Once it loads, copy the URL in the location bar and close the browser.

On the "Northwind.Silverlight" project, right click on "Service References" and click "Add Service Reference". Paste the URL you copy to clipboard into the "Address" field and click "Go". It should then populate the "Services" left pane. Hit OK.


2. Create the XAML. We need to add a DataForm to our XAML. Now, I do not have Expression Blend, so I did mine using Visual Studio. First of all, you need to add a reference to System.Windows.Controls.Data.DataForm. Your XAML should look like this:


<UserControl x:Class="Northwind.Silverlight.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:df="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="450">
<Grid x:Name="LayoutRoot" Background="White">
<df:DataForm x:Name="dfCustomer"></df:DataForm>
</Grid>
</UserControl>

3. Bind the data to the DataForm control. We need to bind the data to our dataform. There are multiple ways on how to go about doing this. In this post I bind the data as soon as the Silverlight application is loading. Or, you can create a button (or whatever event) to initiate the bind. Open "MainPage.xaml.cs" and create a handler method for the "Loaded" event. In this method, basically I will query the data using our data service reference and then bind the result to our dataform.

public partial class MainPage : UserControl
{
Uri baseUri = new Uri("http://localhost:10403/NorthwindDataService.svc", UriKind.Absolute);
public MainPage()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
NorthwindDBDataContext northwindDSContext = new NorthwindDBDataContext(baseUri);

var query = (DataServiceQuery<Customer>)
from c in northwindDSContext.Customers
orderby c.ContactName
select c;

query.BeginExecute((asyncResult) =>
{
try
{
IEnumerable<Customer> result = query.EndExecute(asyncResult);
Dispatcher.BeginInvoke(() =>
{
ObservableCollection<Customer> data = new ObservableCollection<Customer>();
foreach (Customer p in result)
{
data.Add(p);
}
dfCustomer.DataContext = data;
dfCustomer.ItemsSource = data;
});
}
catch (Exception ex)
{
throw ex;
}
}, null);
}
}
Now, we do we need to do all that? It looks overly complicated for just doing a data binding. Why not just do a LINQ query and then bind the result directly? What's "BeginExecute" for? The answer to all that really boils down to because the call to the data service is being down asynchronously. Therefore, the callback is needed. If you look into the method signature of "BeginExecute", you will see that the first parameter is a callback function.

So let's break it down ... see the line where it does this?

query.BeginExecute((asyncResult) => { ... }, null);
That line basically is executing the query and then upon completion of the query and getting the result back (stuffed into the param "asyncResult"), run this generic method (stuff inside the curly braces).

So let's take a look the callback function (stuff inside the curly braces).

IEnumerable<Customer> result = query.EndExecute(asyncResult);
Dispatcher.BeginInvoke(() =>
{
ObservableCollection<Customer> data = new ObservableCollection<Customer>();
foreach (Customer p in result)
{
data.Add(p);
}
dfCustomer.DataContext = data;
dfCustomer.ItemsSource = data;
});
The first line is the "closer" for the async call and it will return a result as an IEnumerable. Next we then using Dispatch.BeginInvoke (because we are transferring data over web service) get the data one by one and put them into an ObservableCollection. Then after that we bind our DataForm to the ObservableCollection.

So far so good? In the next post: Add, Edit, Delete!
-- read more and comment ...

Tuesday, March 31, 2009

Silverlight 3.0, Astoria, LINQ to SQL (1): Setting Up Astoria & LINQ to SQL

Silverlight 3.0 that just came out during MIX09 has a new control called DataForm. In essence, this control binds to an object/collection and from it you can view, edit, add, delete, page, etc. It looks like having a MS Access display your records through its Form.

Mike Taulty (one of the developers in the Silverlight team) made an excellent video showcasing the features and functionality of the DataForm control in Silverlight. You can watch the video here. There is a good blog post recap by Vikram Pendse.

I am not going to repeat what Mike has shown in the video. But what I am going to show through this post is how to build a DataForm that does CRUD (create, read, update, delete) to a LINQ to SQL data layer via ADO.NET Data Service. This first post in the series is to show how to build/prepare the solution, projects, and some ground work in LINQ to SQL and connecting it to ADO.NET Data Service.

What do you need to follow along? Several things:


For the database, I am going to use Northwind. If you do not have Northwind, you can get it from here.

OK, let's begin.
1. Create a Class Library project for our LINQ to SQL project. Why not just create the LINQ to SQL inside a web project for our data service? Because making it in its own project allow us to reuse it. For instance if I want to build a web Dynamic Data project connecting to the same DB, I can just reference this project in my Dynamic Data project. I called project as "Northwind.DalLinq". Add references to "System.Data.Services" and "System.Data.Services.Client".

2. Create a Silverlight project. I named mine as "Northwind.Silverlight".

When you do this, it will then ask you whether you want to host the Silverlight application inside a new website. This website will host not only our Silverlight application, but also the ADO.NET Data Service that talks to our LINQ to SQL. So, I selected "Web Application Project" and hit OK. You can choose "Web Site" or "MVC Web Project" if you want to - it does not really matter.

Add references to "System.ComponentModel", "System.ComponentModel.DataAnnotation", "System.Windows.Controls.Data", and "System.Windows.Controls.Data.DataForm" in the "Northwind.Silverlight" project. Add references to "Northwind.DalLinq" to the "Northwind.Silverlight.Web" project.

So at this point, your solution should look like this:

3. Create a LINQ to SQL class in "Northwind.DalLinq" project and create all table mappings into the dbml, as well as the stored proc. We are going to use them all in this post, but for simplicity, let's just add all. If you feel you know what you're doing, feel free to be more selective.

4. Create an ADO.NET Data Service item in the "Northwind.Silverlight.Web". Right click on the "Northwind.Silverlight.Web" project, "Add New Item", select "ADO.NET Data Service". I name mine as "NorthwindDataService".

Open up "NorthwindDataService.cs" (the codebehind for the ADO.NET Data Service item we just added) and enable it to bind to our Northwind data context. We do this by filling the generic for the DataService type in the class declaration line.
    public class NorthwindDataService : DataService<Northwind.DalLinq.NorthwindDBDataContext>
Then we also need to indicate which table mapping are going to be available to be accessed. We do this by specifying it in this line:
config.SetEntitySetAccessRule("*", EntitySetRights.All);
Right now that will allow ALL to be accessible. This is OK for our exercise, but in reality we want to allow with caution. So your code should look like this:
namespace Northwind.Silverlight.Web
{
public class NorthwindDataService : DataService<Northwind.DalLinq.NorthwindDBDataContext>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{
// TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
// Examples:
config.SetEntitySetAccessRule("*", EntitySetRights.All);
// config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
}
}
}

5. Make sure that your web application is running on a static port, so our Service Reference will be able to find it later. To do this, right click on the "Northwind.Silverlight.Web" project and click "Properties", go to "Web" tab. In here, we want to make sure our port is static by setting it to "Specific Port" instead of the default "Auto-assign Port". You can use any port number that is available.

Alright, right now we already setup our basic LINQ to SQL mapping from our DB, setting up our Silverlight project along with the web application to host it, and lastly we created a new ADO.NET Data Service item in our web application project.

So let's try it! Set "Northwind.Silverlight.Web" project as the StartUp and set "NorthwindDataService.svc" as the Start Page and hit F5!

Most likely you will get this error:

WHAAATTT???

6. Well, no need to panic ... you can set the error to be verbose and it will give you more information. The error is basically caused by ADO.NET confusion about what is the field that holds the primary key of the table mappings that we did in the LINQ to SQL. If you use Entity Framework, it will do it out of the box, but not LINQ to SQL apparently, so we need to decorate the classes with DataServiceKey attribute. Since the dbml generated by LINQ to SQL contains partial classes of the mappings, we can just simply add our own partial class that match with the generated part.

Add a class to our "Northwind.DalLinq" project and call it "NorthwindDB.Custom.cs". So your solution should look like this:

In that class, we declare our partial classes from our DB mappings decorated with the "DataServiceKey" attribute. Here are several examples on how to do them:
namespace Northwind.DalLinq
{
[DataServiceKey("CategoryID")]
public partial class Category { }

[DataServiceKey("TerritoryID")]
public partial class Territory { }

[DataServiceKey(new string[] { "CustomerID", "CustomerTypeID" })]
public partial class CustomerCustomerDemo { }

// TODO: do the rest of the classes
}
Now, if we try hitting F5 again, we then should get a different result:

So now we have our ADO.NET Data Service ready and in my next post I will show you how to hook it up to the DataForm control to display our data.
-- read more and comment ...

Friday, March 20, 2009

Calculating Business Days

A common problem in making custom scheduler application is figuring out how to schedule future items or to predict the next due date. For an example, let's say you are the programmer in UPS (the shipping company wearing everything in brown) - and let say you want to give an ability for the customers to view that if a shipment is shipped on a certain date, it would take 5 business days after the shipment date to get to their door - let's call this a delivery date (an existing functionality). Simple enough, right? If we are just doing this in our head, it sounds pretty easy. But we need to remember that we must skip holidays and weekends - since UPS is only open and operating on BUSINESS DAYS.

There are actually an easy solution to this (which we will see) and I also made a more optimized solution and compared the performance.

We are going to assume that there is a list (List _holidays) that holds the holiday schedule. So our method will be given a start date and an interval - and it should figure out the next date. Here is the code for the easiest solution.


private DateTime ProjectDateByLoop(DateTime startDate, int interval)
{
DateTime endDate = startDate;
int i = 0;
while (i < interval)
{
endDate = endDate.AddDays(1.0);
if (endDate.DayOfWeek != DayOfWeek.Saturday && endDate.DayOfWeek != DayOfWeek.Sunday && (!_holidays.Contains(endDate)))
i++;
}
return endDate;
}
A very simple code, it basically increment the day one by one and check whether it is a holiday or not or whether it falls on a weekend.

As we see above, it is very very simple and it works accurately. In my example above about UPS, this is probably is the best solution for the problem: simple code, easy to maintain, and works! But, as I look into it, I started to wonder what if the interval is in the range of thousands, or millions? It is very unlikely and may never be used ever - but I felt compelled (with the encouragement of certain people) to find a better way of doing this.

So, with that in mind, I produced this code below.

private static int CountDaysInDates(DayOfWeek queriedDay, DateTime startDate, DateTime endDate)
{
TimeSpan ts = endDate - startDate;
// get number of full weeks within dates
int count = (int)Math.Floor(ts.TotalDays / 7);

// get remainder and adjust
int remainder = (int)(ts.TotalDays % 7);
int numOfDaySinceLastQueriedDay = (int)(endDate.DayOfWeek - queriedDay);
if (numOfDaySinceLastQueriedDay < 0) numOfDaySinceLastQueriedDay += 7;
if (remainder >= numOfDaySinceLastQueriedDay) count++;
return count;
}

private DateTime ProjectDate(DateTime startDate, int interval)
{
DateTime endDate = startDate;

if (interval == 0)
endDate = startDate;
else
{
endDate = startDate.AddDays(interval);
int numOfSaturday = CountDaysInDates(DayOfWeek.Saturday, startDate.DayOfWeek == DayOfWeek.Saturday ? startDate.AddDays(1) : startDate, endDate);
int numOfSunday = CountDaysInDates(DayOfWeek.Sunday, startDate.DayOfWeek == DayOfWeek.Sunday ? startDate.AddDays(1) : startDate, endDate);

int numOfHoliday = _holidays.Count(h => h >= startDate && h <= endDate && (h.DayOfWeek != DayOfWeek.Sunday && h.DayOfWeek != DayOfWeek.Saturday));
int offSet = numOfSaturday + numOfSunday + numOfHoliday;
endDate = ProjectDate(endDate, offSet);
}
return endDate;
}
At first glance, the second code should be much faster, since it does not have any loop at all. But, it is not quite as simple as the first code (even though it is still simple). The method CountDaysInDates is basically for counting how many times a day (like "Monday") occurs within 2 given dates. I use this method to calculate how many Saturdays and Sundays (weekends) to skipped. Why not just count the number of weekends? Because there is a likelihood that the start date is on a Sunday which needs to be skipped and counted only as "half" of a weekend. Secondly, I want the code to be flexible enough that if the algorithm is used to skip only Sundays, it can be modified easily.

The method ProjectDate is a recursive method. In it basically I checked the number of Saturdays and Sundays plus holidays and add them together - and then call ProjectDate again with the new start date (after interval was added) with the new interval (Sundays + Saturdays + holidays).

So ... how are the performance of these two algorithms? Amazingly enough, when they are used with small intervals, the performance difference is negligible in real use. So, I created an iteration for 100,000 times to run both and measure the execution time.


private void TargetDateHarness()
{
int totalPasses = 100000;
int interval = 720;
DateTime targetDate;

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i = 0; i <= totalPasses; i++)
targetDate = ProjectDate(DateTime.Today, interval);
sw.Stop();
TimeSpan ts = sw.Elapsed;
testContextInstance.WriteLine("Non-Loop time: " + ts.TotalMilliseconds + " ms");

sw.Reset();
sw.Start();
for (int i = 0; i <= totalPasses; i++)
targetDate = ProjectDateByLoop(DateTime.Today, interval);
sw.Stop();
ts = sw.Elapsed;
testContextInstance.WriteLine("Loop time: " + ts.TotalMilliseconds + " ms");
}
The result:

Non-Loop time: 2018.7745 ms (2 seconds)
Loop time: 53362.9731 ms (53 seconds)

-- read more and comment ...

Wednesday, March 18, 2009

ASP.NET Lightweight Automation Framework - Add, Edit, Delete (3/3)

In the last post, we discussed about LAF & how to test a page with list on it. We did paging, sort, and simple display. Now in this post I will show you how to do a test for Add/Edit and Delete.

Here is the code for testing Add Customer functionality from the UI. The essence of the code is that after navigating to the Customer List page, we click on the "New Customer" link with an expectation that the new customer form will show (so we verify it). Then after the form shows, fill it in with information and click "Save" button. This then should close the form and redisplay the list with the new Customer in it - so we verify that it is in there. At this point, our Add is already working. I included at the same time, a test for common edit & deletion.


[WebTestMethod]
public void AddCustomer()
{
HtmlPage page = new HtmlPage("");

// navigate to Customer
page.Navigate("/Customer/");

// Verify search result
HtmlTableElement gridView = (HtmlTableElement)page.Elements.Find("CustomerListingTable");
Assert.IsNotNull(gridView);

// click new Customer
page.Elements.Find("a", "New Customer", 0).Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify new form exists
Assert.IsTrue(page.Elements.Find("CustomerDetail").IsVisible());
Assert.IsTrue(page.Elements.Exists("Cancel"));

// enter information
page.Elements.Find("FirstName").SetText("AFIRST");
page.Elements.Find("LastName").SetText("ALAST");
page.Elements.Find("MiddleInitial").SetText("A");
page.Elements.Find("DoB").SetText("1/1/1999");
page.Elements.Find("TitleId").SetSelectedIndex(0);

// click submit
page.Elements.Find("Save").Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify entry in list
Assert.IsFalse(page.Elements.Find("CustomerDetail").IsVisible());
Assert.IsTrue(gridView.Rows[1].Cells[(int)ColumnNames.LastName].GetInnerText() == "ALAST");

// click detail
gridView.Rows[1].Cells[0].ChildElements[0].Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// click edit
page.Elements.Find("a", "Edit", 0).Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// edit information
page.Elements.Find("FirstName").SetText("AFIRSTUPDATED");
page.Elements.Find("LastName").SetText("ALASTUPDATED");
page.Elements.Find("MiddleInitial").SetText("B");
page.Elements.Find("DoB").SetText("2/2/2000");
page.Elements.Find("TitleId").SetSelectedIndex(1);

// click submit
page.Elements.Find("Update").Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify entry in list
Assert.IsFalse(page.Elements.Find("IdentityDetail").IsVisible());
Assert.IsTrue(gridView.Rows[1].Cells[(int)ColumnNames.LastName].GetInnerText() == "AFIRSTUPDATED");

// click detail
gridView.Rows[1].Cells[0].ChildElements[0].Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// click delete with confirm
page.Elements.Find("a", "Delete", 0).Click(new CommandParameters(WaitFor.None, PopupAction.ConfirmOK));
System.Threading.Thread.Sleep(1000);

// verify
Assert.IsTrue(gridView.Rows[1].Cells[(int)ColumnNames.LastName].GetInnerText() != "ALAST");
}

This is a very rudimentary test only testing best case scenarios where add, edit, and delete are successful. In my project, I also build tests for if there are any error during saving, or business rules violations, when somebody hit cancel, etc. But, the simple example above should provide enough basis to be built on top of for those other tests.

- Part 2: List, Paging, Sorting
- Part 3: Add, Edit, Delete
-- read more and comment ...

Tuesday, March 17, 2009

Using jQuery to Create Hot-Keys for Web Widgets

I recently created a "dashboard" for one of my project and with jQuery, it was actually pretty easy and fun. Here is a simplified model of what I did.

First of all, you will need to download and include jQuery CORE, jQuery UI, and a hot-key plug-in in your web application. You can get the instruction on how to do that from here.

Here is the basic style I am using to create the boxes:

    .content { width: 450px; }
.dashboard { border: solid 1px #ccc; }
.wrapper { height:100%;}
.displayrestore { clear:both; margin: 0 2px 0 2px; height:100px; overflow:auto; }
.displaymaximize { clear: both; display:none;}
div.maximize { clear:both; width:450px; height: 250px; font-size:2em; background-color: #888;}
div.restore { width:200px; clear:none; float:left; margin-right:8px;background-color: #ddd;}
div.minimize { width:200px; clear:none; float:left; margin-right:8px;}

Here is the script to initialize my hot-keys and setting all the widget boxes to "restore" mode. As you can see that each div is bound to a hot-key ("ctrl-1", etc) and they are all bound to "ctrl-m" to minimize all and to "ctrl-r" to restore all.
        var speed = "normal";
var MainArea = "Main";
var DashboardArea = "Dashboard";

$(document).ready(function() {
for (var i = 0; i < $("#" + DashboardArea).children().length; i++) {
displayRestore($("#" + DashboardArea).children()[i].id.substring(DashboardArea.length));
}
$.hotkeys.add('Ctrl+r', function() {
for (var i = 0; i < $("#" + DashboardArea).children().length; i++) {
displayRestore($("#" + DashboardArea).children()[i].id.substring(DashboardArea.length));
}
});
$.hotkeys.add('Ctrl+m', function() {
for (var i = 0; i < $("#" + DashboardArea).children().length; i++) {
displayMinimize($("#" + DashboardArea).children()[i].id.substring(DashboardArea.length));
}
});
$.hotkeys.add('Ctrl+1', function() { displayCascadeMaximize('ItemOne'); });
$.hotkeys.add('Ctrl+2', function() { displayCascadeMaximize('ItemTwo'); });
$.hotkeys.add('Ctrl+3', function() { displayCascadeMaximize('ItemThree'); });
$.hotkeys.add('Ctrl+4', function() { displayCascadeMaximize('ItemFour'); });
$.hotkeys.add('Ctrl+5', function() { displayCascadeMaximize('ItemFive'); });
$.hotkeys.add('Ctrl+6', function() { displayCascadeMaximize('ItemSix'); });
});

Below is the function to handle maximize event to the widget. It checks first whether there is any widget in maximize mode inside the "main" container and if there is any, restore it then maximize the one intended to be maximized.

function displayCascadeMaximize(source) {
// restore what is currently in max mode
if ($("#" + MainArea).children().length > 0)
displayRestore($("#" + MainArea).children()[0].id);

// display selected widget in main area with maximize view
displayMaximize(source);
}

function displayMaximize(source) {
// create a clone of selected widget and put in main area
$("#" + MainArea).html($("#" + source));

// display maximize mode
$("#" + source + "Maximize").show(speed);
$("#" + source + "Restore").hide(speed);

// adjust css
$("#" + source).removeClass("restore");
$("#" + source).removeClass("minimize");
$("#" + source).addClass("maximize");
}

The handler for restore is pretty straight forward. It checks whether the "source" intended to be restored is in the "main" container or not and based on that execute the correct restore process.

function displayRestore(source) {
if ($("#" + source).parent()[0].id == MainArea) {
// if source is in main area then remove it from main area and show in Dashboard area
$("#" + DashboardArea + source).html($("#" + MainArea).html());
$("#" + MainArea).html("");
$("#" + DashboardArea + " #" + source).show(speed);

if ($("#" + DashboardArea + " #" + source).hasClass("restore")) { }
else {
$("#" + DashboardArea + " #" + source + "Maximize").hide(speed);
$("#" + DashboardArea + " #" + source + "Restore").show(speed);

$("#" + DashboardArea + " #" + source).addClass("restore");
$("#" + DashboardArea + " #" + source).removeClass("minimize");
$("#" + DashboardArea + " #" + source).removeClass("maximize");
}
}
else {
// if source is in Dashboard area
$("#" + source + "Maximize").hide(speed);
$("#" + source + "Restore").show(speed);

$("#" + source).addClass("restore");
$("#" + source).removeClass("minimize");
$("#" + source).removeClass("maximize");
}
}

The handler for restore is pretty much the same as restore - except it executes minimize instead of restore.

function displayMinimize(source) {
if ($("#" + source).parent()[0].id == MainArea) {
// if source is in main area then remove it from main area and show in Dashboard area
$("#" + DashboardArea + source).html($("#" + MainArea).html());
$("#" + MainArea).html("");
$("#" + DashboardArea + " #" + source).show(speed);

if ($("#" + DashboardArea + " #" + source).hasClass("minimize")) { }
else {
$("#" + DashboardArea + " #" + source + "Maximize").hide(speed);
$("#" + DashboardArea + " #" + source + "Restore").hide(speed);

$("#" + DashboardArea + " #" + source).addClass("minimize");
$("#" + DashboardArea + " #" + source).removeClass("restore");
$("#" + DashboardArea + " #" + source).removeClass("maximize");
}
}
else {
// if source is in Dashboard area
$("#" + source + "Maximize").hide(speed);
$("#" + source + "Restore").hide(speed);

$("#" + source).addClass("minimize");
$("#" + source).removeClass("restore");
$("#" + source).removeClass("maximize");
}
}

The HTML:
    








Item One in Restore Mode


Item One in Maximize Mode

 






Item Two in Restore Mode


Item Two in Maximize Mode

 






Item Three in Restore Mode


Item Three in Maximize Mode

 






Item Four in Restore Mode


Item Four in Maximize Mode

 






Item Five in Restore Mode


Item Five in Maximize Mode

 






Item Six in Restore Mode


Item Six in Maximize Mode

 






-- read more and comment ...

Monday, March 16, 2009

ASP.NET Lightweight Automation Framework - List, Paging, Sorting (2/3)

In the last post, I wrote about LAF, difference between LAF and unit test, controller test, etc. Now we are going to get into on how to user LAF, especially in ASP.NET MVC project.

In my demo project I have a Customer list (it can be any list) that looks like this:

So I want to test all web functionality that an end user will and can possibly do. How do I do it with LAF?

First, create a web application to run your LAF project. ou can follow the instruction to do that here.

After you got that setup, create a class file and start writing tests. So in my example, I want to tests for simply displaying the list, sorting, paging, clicking "New", clicking "Select", and clicking "Delete".

OK, let's begin with displaying the list:


[WebTestMethod]
public void FullList()
{
HtmlPage page = new HtmlPage("");

// navigate to Customer list page
page.Navigate("/Customer/");

// Verify search result
HtmlTableElement gridView = (HtmlTableElement)page.Elements.Find("CustomerListingTable");
Assert.IsNotNull(gridView);
}
So a very simple test where basically I navigate to the Customer page and make sure it displays my table. I can do additional checks like whether there are 10 rows in my list, whether the first record is correct, etc. We will see how to do those things in the following test examples.

So, how about testing Sorting functionality? In our list, a user should be able to click on the header and sort the data based on that field ascendingly and then descendingly.

[WebTestMethod]
public void SortByFirstName()
{
HtmlPage page = new HtmlPage("");

// navigate to Customer list page
page.Navigate("/Customer/");

// Verify search result
HtmlTableElement gridView = (HtmlTableElement)page.Elements.Find("CustomerListingTable");
Assert.IsNotNull(gridView);

// sort by first name desc
gridView.Rows[0].Cells[(int)ColumnNames.FirstName].ChildElements[0].Click(WaitFor.None);
// wait for my jQuery ajax
System.Threading.Thread.Sleep(1000);

// verify
Assert.IsTrue(gridView.Rows[1].Cells[(int)ColumnNames.FirstName].GetInnerText().CompareTo(gridView.Rows[2].Cells[(int)ColumnNames.FirstName].GetInnerText()) >= 0);

// sort by first name asc
gridView.Rows[0].Cells[(int)ColumnNames.FirstName].ChildElements[0].Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify
Assert.IsTrue(gridView.Rows[1].Cells[(int)ColumnNames.FirstName].GetInnerText().CompareTo(gridView.Rows[2].Cells[(int)ColumnNames.FirstName].GetInnerText()) <= 0);
}
What happen in the code above is quite simple; after navigating to the Customer list page, the test verify whether the grid/table exists. After that, it navigate within the table and click the header of the first name column (I enumerated my columns). Then I verify the result by comparing the first name data between the 1st record and the 2nd record according to the sort direction.

Now let's test our paging. There are multiple ways to page through the data according to our list. A user can click on the page number, or click on a previous or next link, and they can do it through the top page numbers or the bottom one.

[WebTestMethod]
public void PageByNumberTop()
{
HtmlPage page = new HtmlPage("");

// navigate to Customer list page
page.Navigate("/Customer/");

// Verify search result
HtmlTableElement gridView = (HtmlTableElement)page.Elements.Find("CustomerListingTable");
Assert.IsNotNull(gridView);

// get the paging links
HtmlElement topPaging = page.Elements.Find("pagination-clean-top");

// verify page 1 and goto page 2
page.Elements.Find("toppage2").Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify page 2 and goto page 3
Assert.AreEqual("2", topPaging.ChildElements[2].GetInnerText());
page.Elements.Find("toppage3").Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify page 3 and goto page 4
Assert.AreEqual("3", topPaging.ChildElements[3].GetInnerText());
page.Elements.Find("toppage4").Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify page 4 and goto page 1
Assert.AreEqual("4", topPaging.ChildElements[4].GetInnerText());
page.Elements.Find("toppage1").Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify page 1 and goto page 3
Assert.AreEqual("1", topPaging.ChildElements[1].GetInnerText());
page.Elements.Find("toppage3").Click(WaitFor.None);
System.Threading.Thread.Sleep(1000);

// verify page 3
Assert.AreEqual("3", topPaging.ChildElements[3].GetInnerText());

}

What happen on the code above is basically I queried the page number link and click it. From the default page 1, to page 2, then page 3, then page 4, to test consecutive paging. After that, from page 4 jumped to page 1, then jumped to page 3. Obviously, there are more variety of these that we can do, but hopefully you get the idea. Also, in this code, I am just testing on paging by number for if the users are just paging using the page numbers on the top. To test the previous or next link, the code should be pretty similar to this.

- Part 1: Overview
- Part 3: Add, Edit, Delete
-- read more and comment ...

Not on The Menu: Quesadilla at Chipotle

Last night I went to Chipotle for dinner with my wife and son. There were like 5 or 6 people a head of me in the line - so I took my time to ponder what I want to order.

When it was about just 2 more person ahead of me, I saw the lady behind the counter did something I have never seen before in Chipotle - she put cheese on the tortilla then fold it and put it on the tortilla press/warmer.

Then the next guy - the guy who is directly in front of me - said something like this "Same thing - chicken quesadilla." After hearing that, I reread the menu on the big board again and again and again - and I was pretty sure that quesadilla was not on the menu.

So now, it was my turn to order, I ordered my half chicken and half carnitas salad and then I said, "Can I have a small cheese quesadilla for my son?" As I was expected, the lady said, "Sure. Just cheese?" and I replied back "Yes, just cheese."

As I walked down and selected my choices of salsa, sour cream, and cheese, I glanced back at the menu trying to find the word "quesadilla" - but it was nowhere to be found. Then at the cashier register, the cashier lady punched the buttons and I briefly saw that there was an entry for my small chicken quesadilla - it was also in the receipt - for $1.00. I am not sure how much the big quesadilla is or if you add meat to it.

So there you go, Chipotle has quesadilla - btw the small cheese quesadilla is great for kids!

Some Chipotle tips & tricks:
1. If you are getting a carry out, order online!
2. Instead of getting a fajita wrap, get a fajita bol WITH tortilla on the side. On the bowl, you can get extra stuff without the fear of tearing your tortilla. Plus it is not as messy and easier to bring home if you cannot finish it.
3. You can get half & half meat. So you can get chicken & steak or steak and carnitas, etc. They will charge you for the higher price ($0.50 difference I think between chicken and steak/carnitas).
4. You can get more than one type of salsa. So get corn salsa AND verde AND the hot salsa if you want - or nay combination thereof.
5. If you are ordering a salad and feel that it is kinda small - so add stuff to it (but no beans or rice). I usually add onions/peppers and salsa combination.
-- read more and comment ...

Sunday, March 15, 2009

ASP.NET Lightweight Automation Framework - Overview (1/3)

ASP.NET QA Team released the Lightweight Automation Framework on mid Feb 2009 - and I have been using it to test the project I am working on (an ASP.NET MVC project). Suffice to say that I am thrilled and having fun with it (and actually caught some bugs because of it!!).

In this post I will outline the basic of Lightweight Automation Framework (LAF) and in the following posts I will put some sample code, quirks, how to, etc.

What is Lightweight Automation Framework?
LAF is an internal test tool that ASP.NET QA team use to do regression testing on ASP.NET feature. Remember that it is a tool, so you still have to build your tests, but this framework allows you to build and run or do your tests easily and in a more comprehensive manner - and most importantly, it is automated.

How does LAF differs from regular unit test or other test project such as MVC controller tests?
LAF is a regression test tool - so it suppose to test the web application from user's perspective. This would be the tool to use to implement all your use-case scenarios testing. So the scope is much larger compared to unit test.

Another thing that we will see is that LAF actually runs as within an ASP.NET application - instead of just running/executing code. LAF will actually read and manipulate the DOM in the browser (you can run in IE, FF, Safari or Opera). So when you run a typical test in LAF, it will look like someone is running the web application in a browser and clicking this, entering data, etc etc.

This is something wonderful with LAF, since what used to be tedious and hard to be done - end user testing - now can be automated (by code).

Where can I get it?
You can download the source code in its codeplex page as well as read further about it.

ASP.NET QA Team also has a blog here.

- Part 2: List, Paging, Sorting
- Part 3: Add, Edit, Delete
-- read more and comment ...

Tuesday, February 17, 2009

Saving Money on Electricity & Gas Bills

A friend of mine just told me about his gas bill for last month - a whooping $600+. Good thing that he lives in the house with 6 other housemates and the house is also an older house - so there are probably some leaks here and there which creating more work for the heater, hence more expensive payment.

Another fellow also is paying about $150+ per month for his electric bill per month.

I used to never really consider them as a big deal until I moved into a house, got married, and had a son. My expenses are piling up and I need to tighten my budgeting and do some cost cutting. One of the first area that I was able to successfully reduce is in the area of utility bills.

My gas bill used to be about $300 during the winter months and about $70 per month for the rest of the year. Last year, I reduce my gas bill from averaging $300 per month between December to March to about $150 per month (so about 1/2 of it used to be).

So how did I do it? Here are several tips that I found to be pretty useful...

Gas
I live in a 4 bedroom house, but we in most nights we only use 2 of the bedrooms: for my wife and I, and another room for my son. We use another room as my office and the last one as a guess bedroom. So keep in mind that the suggestions below may or may not apply to you in all situations - I am just providing tips that so far have been working pretty well for me and my family.

1. Get a programmable thermostat
This may cost you between $30 to $200, depending on how fancy you want to get with your thermostat. At least get a programmable one for both heat and cold air - and can be programmed based on daily basis. Some only has weekday or weekend setting. Depending on your need, get whatever suited for you. Mine is programmable by day and it is only $40 (one of the best investment in home improvement I have made).

2. I programmed it to turn down the temperature during the day on several days of the week where nobody is at home. Do not turn your heater off, but just lower the temperature. I lower mine to 10-15 degrees cooler than the regular temperature. So when I am at home, the thermostat usually shows 72 F and ~60 F when nobody is at home. You can program it so that it goes back up to the regular comfortable temperature 30 minutes before you are back home so you will come home to a warm house instead to a cold one. Obviously you cannot consider all possibilities, but try to get the "typical" schedule - and then program them it - you can override them as necessary.

3. If you are going away for a longer period of time (like on a vacation), lower your house temperature to the upper 40s F (but do not turn off your heater) and set it to come back up when you are about to come back.

4. If your household is like mine, where we typically are only using 2 rooms at night (or less), you may want to consider buying an oil portable heater. So at night, schedule your main house thermostat to lower the temperature to the 60s F and turn on your oil heater in your bedroom(s). That way, you are only heating the necessary rooms (typically your bedrooms) and not wasting energy on other unused rooms. I usually turn down the house temperature at 9:30pm and turn on the oil heater in my bedroom around the same time. Since the heat dissipates gradually in the main house so I won't notice much difference when I am walking into my bedroom (typically around 10:30pm) where the temperature is already picked up my the oil heater. My oil heater has a timer so it shut itself down around 7am, while my main house thermostat increase the temperature of the house around the same time (up to 72 F).

5. Reduce leaks. Make sure your doors, windows, etc are not leaking. You can buy weather strips at the hardware store to stop the leaks from doors and windows.

6. Install a ceiling fan. Heat goes up and ceiling fan helps to circulate the hot air from your heater better. You do not need to do this on every room of your house, but it will be very helpful to have it in your most used rooms in the house. Secondly, during the non-winter times, it can also give you some extra breeze.

There are other things you that requires much more money, such as replacing your windows with double layer windows, installing screen doors, fixing wall insulation, etc. I am going to get into those things in this post. Another thing you can do is put on an extra layer of clothing and lower the temperature. One of my friend set his daily temperature at 68 F during the winter months - and always wear a sweater or extra layer of clothing at home. Works great for him!

Electric
The principle is no different than "gas" - less usage means less payment.

1. Change filament based bulb with CFL bulbs. They cost more to buy, but they are worthy investments in the long run. Especially if you live in a house, where there are tons of bulbs. CFL bulbs usually cost you $2 per bulb but they also last longer than filament bulbs. In most cases, your average house filament bulb is a 60W bulb. Using CFL, you can get the same brightness with just 15W - that is 75% energy saving per bulb.

2. There are some light fixtures or lights that cannot use CFL bulbs. Light fixtures with dimmers cannot use CFL bulbs. Also, some of my lights are using fluorescent lights. In those areas, I would try to find the lower energy bulbs/lights (substitute 40W fluorescent with 34W, etc). Read more about CFL bulb here.

3. Always turn-off when unused. You can get a smart switch that automatically turn off the light when the room is unused for a period of time. OR ... you can just create a good habit of turning off the light when you are not using the room. In my house a good example of this is the kitchen, where there are 4 strips or 40W fluorescent light on the ceiling - totally to a 160W energy used when it is on. You can reduce the energy usage to 0 if you turn it off. Of course this also applies to other appliances and electronics, such as TVs, computers (or you can set it to hibernate automatically), DVD players, etc.

-- read more and comment ...

Saturday, February 14, 2009

Future Quarterback?

Once sport that Jon was exposed to since early on is football. He basically grew up knowing that Ohio State is THE team in football and yelling "O-H" "I-O" is something considered to be much more precious than "da-da". His first ball is an Ohio State football and his first jacket is an OSU jacket.

Anyway, I took some pictures of him playing with buddy Andrew on Monday - and surprisingly he could hold the football correctly (most of the time) and threw some spiral several times.

So here are some pictures:

Call plays and reading defense ....


Ball is snapped ...




Fake pump to the right!


It's a Quarterback Draw!


He runs and he scores!!! Touchdown!!!


-- read more and comment ...

Tuesday, February 10, 2009

Outlook Filter/Rule to Remove Spam from YOURSELF?

My MS Outlook Exchange account is littered with SPAM - so I turned on the junk email filter on. This enables the auto rules that move "junk"-ish email into the junk mail folder. It does not move all junk email to junk mail folder but for the ones that pass through the filter I can individually right click and include them as junk, so the next time the same email address sends me anything, it will be considered as junk.

So, I am golden, right? Yes ... for a while ... until I start to get junk emails from MYSELF. I am pretty sure that my computer is clean from adwares, viruses, etc. So, it must be that the spammers have gotten my email address and they are spamming me using it. If I try to include those email in the blocked senders list, I can't - since the sender's address is my own email address.

To handle this, initially I create a filter to block certain subjects (since they are all have similar subjects). This works for a while. But as time goes, I have to keep adding and adding more subjects into my rules - which becomes very large. There must be an easier way!

Here is how I solved the problem above:

I created a new rule in Outlook, and the rule is basically checking my email header. So I check for email sent by my email address but is not sent from my Exchange server and move those to junk mail folder (and stop processing any more rules). Voila! Now my Inbox is clean and I can remove my bloated "subject" rule.
-- read more and comment ...

Hot Chicken Chowder

I made spicy chicken chowder last Saturday and it turned out great. The (already awesome) original recipe is from the food network - by Paula Deen and I put my own twist into it - although the main essence is still the same.

Here are some of the changes I made:

  • From the original recipe, it calls for 2 large onions. I used 3 large onions.
  • Instead of all poblano peppers, I used a combination of poblano, cubano, and jalapeno - and I left some seeds in to give more heat. If you want - you can roast the peppers first, make it even tastier.
  • Instead of 3 quarts of broth, I only used between 2 and 2.5 quarts. This will render thicker soup (depending on your taste, you may or my not like this). If you prefer thinner soup, you can use 3 quarts like the original recipe says.
  • Since I love cilantro, I added more to it during serving time.
  • If you want - you can add corn to it.


Now here is the kicker ... it tastes even better the second day - so my advice is to make it at night and eat it for lunch the next day! Get a cuban bread and you are set!
-- read more and comment ...

Monday, February 9, 2009

Dynamic LINQ Library

Let's say you have an application that has a list page that displays a collection of records (i.e. product list)- and on this list page, you want to be able to sort based on the field headers by clicking it. If clicked again, it would reverse the sort direction/order (ascending/descending) - just like Windows Explorer.In regular LINQ - this is not so easy, since LINQ is strongly typed. So let's take a look how would we do this in regular LINQ. Getting all Products by CategoryId = 5 ordering them by SupplierId

 
var products = from p in Products  where p.CategoryId == 5  order by p.SupplierId  select p;
Now if we want to order it descendingly by SupplierId I would write in LINQ:
 var products = from p in Products  where p.CategoryId == 5  order by p.SupplierId descending  select p;
So not bad. But what if we want to sort by UnitPrice?
 var products = from p in Products  where p.CategoryId == 5  order by p.UnitPrice  select p;
How about sort by ProductName?
 
var products = from p in Products  where p.CategoryId == 5  order by p.ProductName  select p;
So if we want to have an option to make our product list sortable by ProductName or UnitPrice or Supplier, we would write something like this in our code:
 
...
var products;
switch (sortfield){
 case "ProductName":
  products = from p in Products
    where p.CategoryId == 5
    order by p.ProductName
    select p;
  break;
 case "UnitPrice":
  products = from p in Products
    where p.CategoryId == 5
    order by p.UnitPrice
    select p;
  break; 
case "SupplierId":
  products = from p in Products
    where p.CategoryId == 5
    order by p.SupplierId
    select p;
  break;
}
...
Which is OK - but what if we have 10 sortable fields? It will be a super long code and very repetitive. So how can we solve this problem with proper refactoring while still using LINQ? This is where LINQ Dynamic Library saves the day!First of all, you need to download the library:
  • VB - under \LINQ Samples\DynamicQuery\ directory
  • C# - under \LINQ Samples\DynamicQuery\ directory
After the file is downloaded, include the Dynamic.cs (or vb) in your project. Now everytime you want to use dynamic linq, you will need to reference the namespace System.Linq.Dynamic in your code (or you can use a using statement - or Import in vb). Now using the dynamic library, the code above can be transformed into something like this:
 
...
var products = Products
   .Where(p => p.CategoryId == 5)
   .OrderBy(sortfield);
...
It is much more concise, clean, and configurable. So there you go, LINQ Dynamic Library - extremely useful!You can learn about LINQ hereScott Guthrie has an excellent post about LINQ Dynamic Library here
-- read more and comment ...