Steven Nagy .NET

Thursday, 14 June 2007

Generic Collections in .Net 2.0

One of the things I have discovered about ASP.NET developers is that they don't have a firm understanding of the difference between VALUE types and REFERENCE types. While I'm not interested in dispensing that information here (because other people have done it before way better than I ever could) I do want to demonstrate something that came up at work today relating to reference types.

For confidentiality reasons I can't provide the same code, so I will come up with an example that is similar (demonstrates the same concepts).

Consider this: I want to write a method that connects to a database and returns a generic list of "Customer" objects, and also for each customer, I want to store a generic list of "Order" objects. The Customer object contains a CustomerID and a generic list of Order objects. There would be other information but its not important at this stage.

My SQL or Stored procedure will return 2 result sets from 1 call for performance reasons. The first contains all the customers, the second contains all the orders with a customer ID for each order.

I need to read from my data reader all the customers first (since this is the first result set) and then I need to read all the orders, and assign them to the correct customer object. I already have a method that takes a reader object and returns the next customer object. I also have a method that takes the reader and returns an Order object. What we are interested in is how to assign an order to a customer long after all the customers have been loaded?

Here's the first pass at the code:

List<Customer> AllCustomers =
        new List<Customer>();
SqlDataReader reader =
        DatabaseHelper.GetReaderForCustomers();

while (reader.Read()) {
    Customer customer = GetNextCustomer(reader);
    AllCustomers.Add(customer);
}
reader.NextResult();
while (reader.Read()) {
    Order order = GetNextOrder(reader);
    foreach (Customer c in AllCustomers) {
        if (c.CustomerID == order.CustomerID) {
            c.Orders.Add(order);
        }
    }
}
reader.dispose();
return AllCustomers;

Looking at the above code, we are creating a list of customers, and then as we load each order object, we are finding the right customer to add the order to. The 'foreach' loop is an expensive operation in the above example, yet is probably something we find ourselves doing quite a lot in our code. Is there a better way? Well we could add a "break" statement in when we find the right customer so that the loop doesn't finish. But there still must be more we can do?

Enter: the Dictionary! This is another generic collection, that allows you to choose what "type" the value AND the key will be. Dictionaries are fast to lookup data based on their key, so are ideal for our situation. We need to keep our generic List for the return type of the method, but we can also utilise a dictionary for storage. Consider the new code:

List<Customer> AllCustomers = new List<Customer>();
Dictionary<int, Customer> customersByID =
        new Dictionary<int, Customer>();
SqlDataReader reader =
        DatabaseHelper.GetReaderForCustomers();

while (reader.Read()) {
    Customer customer = GetNextCustomer(reader);
    AllCustomers.Add(customer);
    customersByID.Add(customer.CustomerID, customer);
}
reader.NextResult();
while (reader.Read()) {
    Order order = GetNextOrder(reader);
    customersByID[order.CustomerID].Orders.Add(order);
}
reader.dispose();
return AllCustomers;

Great! And if you understand the basics of reference types, you'll understand that this code works. If not, you might be asking: "How does adding the order to the Dictionary item also add to the List item?"

Well, as I said above, you need to understand reference types. The "Customer" object is the same object, whether it is a variable in my method, an item in the dictionary, an item in the list, a parameter to a method, etc. Its the SAME customer object regardless.

Well that's all I want to say about that really. Check out the System.Collections.Generic namespace for other generic collections.

Technorati Tags: , , ,

0 Comments:

Post a Comment



<< Home