Coding and Dismantling Stuff

Don't thank me, it's what I do.

About the author

Russell is a .Net developer based in Lancashire in the UK.  His day job is as a C# developer for the UK's largest online white-goods retailer, DRL Limited.

His weekend job entails alternately demolishing and constructing various bits of his home, much to the distress of his fiance Kelly, 3-year-old daughter Amelie, and menagerie of pets.

TextBox

  1. Fix dodgy keywords Google is scraping from my blog
  2. Complete migration of NHaml from Google Code to GitHub
  3. ReTelnet Mock Telnet Server à la Jetty
  4. Learn to use Git
  5. Complete beta release FHEMDotNet
  6. Publish FHEMDotNet on Google Code
  7. Learn NancyFX library
  8. Pull RussPAll/NHaml into NHaml/NHaml
  9. Open Source Blackberry Twitter app
  10. Other stuff

Cooking with .Net Lambdas Part 1 - Covering Up Them Code Smells

.Net Lambas are awesome. I know a few folks who maybe don't share the sentiment, but to me they thoroughly kick ass. I just need to look at projects such as Fluent NHIbernate, or Moq, and it's pretty clear that Lambas have been one of the most rewarding language extensions ever, period. But I'm going to hazard a guess that if you're a C# developer and you're reading this, you've used lambas many times in things like a "List<T>.Where(...)", but you haven't got a clue what the heck that "Func<T, string>" stuff is all about.

I'm not going to give you a nitty-gritty low level explanation - there are plenty of those already knocking around, so instead I'm going to just going to give a nice step-by-step example.

Some Background

We have a fairly big smell in our code-base around the way we handle collections vs deliveries. Without going into too much detail (industrial espionage ahoy!), let's say we have a single object that represents a product on an order. That object can be for delivery or collection, so we may have an order that looks something like:

 

Order order = new Order();
order.Items = new List<Item> {
    new Item {DeliveryDate = new DateTime(2011, 07, 01)},
    new Item {DeliveryDate = new DateTime(2011, 07, 01)},
    new Item {CollectionDate = new DateTime(2011, 07, 02)}
};

In our app, we need to give a user the ability to change a DeliveryDate or a CollectionDate on each of the products on the order, and then to generate two sets of audit records – one listing the items that had their DeliveryDate changed, and one listing the items that had their CollectionDate changed.

 

The Problem

Important Caveat - None of the following code samples have been tested! But they should be 99% there. Given the above requirement, we start coding the logic, and we end up with something like the following for handling DeliveryDates (Incidentally, this is a simplified example of the real problem):

 

public void SaveNotes(Order orderBeforeUpdate, Order orderPostUpdate)
{
  GenerateDeliveryNotes(orderBeforeUpdate, orderPostUpdate);
  // TODO - GenerateCollectionNotes(orderBeforeUpdate, orderPostUpdate);
}

private void GenerateDeliveryNotes(Order orderBeforeUpdate, Order orderPostUpdate)
{
  foreach (var delivery in GetChangedDeliveries(beforeOrder, afterOrder);
  {
    String.Format("Delivery for item {0} changed from {1} to {2}",
      delivery.ItemCode, delivery.FromDate, delivery.ToDate);
    noteList.Add(newNote);
  }
}

private IEnumerable GetChangedDeliveries(Order orderBeforeUpdate, Order orderPostUpdate)
{
  select from beforeItem in orderBeforeUpdate.Items
    join afterItem in orderAfterUpdate.Items
      on beforeItem.Id equals afterItem.Id
    where beforeItem.DeliveryDate != afterItem.DeliveryDate
    select new ChangedDeliveryDate
    {
      ProductCode = beforeItem.ProductCode,
      PreviousDate = beforeItem.DeliveryDate,
      NewDate = afterItem.DeliveryDate
    };
}

 

Okay, it's not a trivial piece of code, and as I mentioned above, it's not even as long-winded as the live solution, where there are a few more classes involved. But at least it's done. Do we just copy-and-paste the above GetChangedDeliveries method and change the references to "DeliveryDate" to "CollectionDate"? Hell no, let's fix it with lambdas.

Lambdas To The Rescue

What we need is a little delegate that will take an Item class, and return a DateTime value. We also need a nice expressive way to generate said delegate, which is where Lambdas come in. Let's change our GetChangedDeliveries method to introduce a delegate as follows:

private IEnumerable GetChangedDeliveries(Order orderBeforeUpdate, Order orderPostUpdate,
  Func<Item, DateTime> selectDate)
{
  select from beforeItem in orderBeforeUpdate.Items
    join afterItem in orderAfterUpdate.Items
      on beforeItem.Id equals afterItem.Id
    where selectDate(beforeItem) != selectDate(afterItem)
    select new ChangedDate
    {
      ProductCode = beforeItem.ProductCode,
      PreviousDate = selectDate(beforeItem),
      NewDate = selectDate(afterItem)
    };
}

The magic bit above is the "Func<Item, DateTime>" - this is saying we want a Lambda that operates on an object of type "Item" (the intillisense after the => bit is driven by this), and which must return a DateTime value. We can now rewrite our GenerateDeliveryNotes method as follows:

private void GenerateDeliveryNotes(Order orderBeforeUpdate, Order orderPostUpdate)
{
  foreach (var delivery in GetChangedDeliveries(beforeOrder, afterOrder, x => x.DeliveryDate);
  {
    String.Format("Delivery for item {0} changed from {1} to {2}",
      delivery.ItemCode, delivery.FromDate, delivery.ToDate);
    AuditService.Add(newNote);
  }
}

And to add the code for our Collection notes, we just need to copy the above five-line method as follows:

private void GenerateDeliveryNotes(Order orderBeforeUpdate, Order orderPostUpdate) {
   foreach (var delivery in GetChangedDeliveries(beforeOrder, afterOrder, x => x.CollectionDate);
   {
     String.Format("Collection for item {0} changed from {1} to {2}",
       delivery.ItemCode, delivery.FromDate, delivery.ToDate);
     AuditService.Add(newNote);
   }
 }

Categories: Architecture
Permalink | Comments (0)

Pingbacks and trackbacks (1)+

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading