Tuesday, July 22, 2008

LINQ to Objects on the Compact Framework - Part 1

I wrote an article recently about writing comparer classes to sort data on the CF because LINQ to SQL is not supported on the Compact Framework. I did forget to mention LINQ to Objects which *is* supported on the Compact Framework and of course adds so much more power than writing a comparer class.

So just as in the previous article we have a simple customer object:
public class CustomerInfo
{
public int CustomerId
{
get;
set;
}

public string Name
{
get;
set;
}

public string AddressLine1
{
get;
set;
}

public string AddressLine2
{
get;
set;
}

public string AddressLine3
{
get;
set;
}

public string City
{
get;
set;
}

public string County
{
get;
set;
}

public string PostalCode
{
get;
set;
}

public string Country
{
get;
set;
}
}
So instead of writing all that comparer logic, instead we can use LINQ to Objects as follows:
[MTAThread]
static void Main()
{
List<CustomerInfo> customersSource = new List<CustomerInfo>
{
new CustomerInfo {CustomerId = 1, Name = "Joe", City = "London"},
new CustomerInfo {CustomerId = 2, Name = "Pete", City = "Paris"},
new CustomerInfo {CustomerId = 3, Name = "John", City = "New York"},
new CustomerInfo {CustomerId = 4, Name = "Pete", City = "London"},
new CustomerInfo {CustomerId = 5, Name = "Paul", City = "Dublin"},
new CustomerInfo {CustomerId = 6, Name = "Steve", City = "Exeter"},
new CustomerInfo {CustomerId = 7, Name = "Clare", City = "Norwich"}
};

List<CustomerInfo> customersAscending =
(from c in customersSource
orderby c.City ascending
select c).ToList();

List<CustomerInfo> customersDescending =
(from c in customersSource
orderby c.City descending
select c).ToList();

Debug.WriteLine("Before sort");
DisplayCollection(customersSource);
Debug.WriteLine("");

Debug.WriteLine("After sort - Ascending");
DisplayCollection(customersAscending);
Debug.WriteLine("");
Debug.WriteLine("After sort - Descending");
DisplayCollection(customersDescending);

}

public static void DisplayCollection(List<CustomerInfo> customers)
{
foreach (CustomerInfo customer in customers)
{
Debug.WriteLine(string.Format("Name: {0}, City: {1}",
customer.Name, customer.City));
}
}
Output of the above looks like the following:
Before sort
Name: Joe, City: London
Name: Pete, City: Paris
Name: John, City: New York
Name: Pete, City: London
Name: Paul, City: Dublin
Name: Steve, City: Exeter
Name: Clare, City: Norwich
After sort - Ascending
Name: Paul, City: Dublin
Name: Steve, City: Exeter
Name: Joe, City: London
Name: Pete, City: London
Name: John, City: New York
Name: Clare, City: Norwich
Name: Pete, City: Paris
After sort - Descending
Name: Pete, City: Paris
Name: Clare, City: Norwich
Name: John, City: New York
Name: Joe, City: London
Name: Pete, City: London
Name: Steve, City: Exeter
Name: Paul, City: Dublin
So exactly the same results as both the IComparer and LINQ to SQL examples.

Of course LINQ is a little bit more powerful than traditional IComparer code in that we can filter data without having to write much code or go back to the database - which is quite powerful.

What if we wanted to show customers from London. We could write a query as:
 List<CustomerInfo> customersFromLondon = 
(from c in customersSource
where c.City == "London"
select c).ToList();
Debug.WriteLine(string.Format("Customers from london: {0} customers
found", customersFromLondon.Count.ToString()));
DisplayCollection(customersFromLondon);
The output of the above is as follows:
Customers from london: 2 customers found
Name: Joe, City: London
Name: Pete, City: London
Bearing in mind these examples are only 1 dimensional objects, you can see the power when we have related objects. I will show some more complex examples in another post.

We can also do wildcard filtering such as show me customers that name begins with 'J':

List<CustomerInfo> customersNameStartsWithj =
(from c in customersSource
where c.Name.StartsWith("J")
select c).ToList();
Debug.WriteLine(string.Format("Customer that name begins with the letter 'J': {0}
customers found", customersNameStartsWithj.Count.ToString()));
DisplayCollection(customersNameStartsWithj);
The output of this is as follows:
Customer that name begins with the letter 'J': 2 customers found
Name: Joe, City: London
Name: John, City: New York
Which gives us two results as with have a John and a Joe in our list of data.

Typically though, that list would normally have been returned from a data layer in your application and instead of going back to the database to filter, sort etc, you'd use LINQ instead which will improve performance no-end. This is also quite powerful if you are retrieving your data from a remote server, you wouldn't want to do another round trip to filter or sort results.

The really exciting thing about all this code is that it will run against your existing custo strongly typed collections and that is very powerful.

UPDATE: See part 2 of this series here.

No comments: