Monday, August 11, 2008

A great use for extension methods

It seems like there is a lot of confusion out there about extensions methods. Some people say that it is bad programming practice to use them. I have also heard they were added solely to support LINQ and your average programmer should not have any use for them. Furthermore, why would you possibly want to add an Extension Method to your own custom class when you have the source code and could just add the method directly to the class?
Well, I think I have found the perfect use for Extension Method in tiered application development. I strongly believe in separating business objects, data access, logic, and UI development into distinct layers. And using Extension Methods allow greater ease in adhering to this approach. I will show you how it did for me.
First I want to briefly explain what an Extension Method is and then I will show an example of how I used extensions methods.

Extension Methods in a nutshell

An Extension Method is nothing more than a compile time feature. That is it. Nothing more! You don’t really get to magically add a method to any class that you choose nor get access to private members. When you create an Extension Method, you are simply registering a static method (or shared in VB) for the compiler to reference when it encounters that method name after an object variable reference. Here is an example of adding an Extension Method called print to a class named Contact:
    <Extension()> _
    Sub PrintLastName(ByRef contact As Contact)
        Console.WriteLine(contact.LastName)
    End Sub
In order to use this Extension Method I would need to make sure that my code imports the Name Space that the extension was defined in and the following code.
   Dim c As Contact = New Contact() With { _
       .FirstName = "John", _
       .LastName = "Smith", _
       .Phone = "214-844-1212"}

   'Call extension method
   c.PrintLastName()
However the call to the Extension Method PrintLastName is really being changed by the compiler to:
    PrintLastName(c)
That is it. It really is that simple. The compiler moves the object reference to the first parameter of the Extension Method call. If you don’t have the method in your imports section or in your current name space, you don’t see it, just like any other Sub. When you use the Extension Attribute, the Visual Studio IDE adds your Extension Method to Intelisence and makes it look it is part of the class. The actual class, Contact in this case, remains completely unchanged and unaware of this new Method.

My scenario and use of Extension Methods

For this example I have a solution with four projects, one for each layer. These are logical layers and the client application is deployed with a DLL from each project.
The first layer is for Business Objects and this layer’s only responsibility is to define what needs to be contained in the class. Nothing else! No database calls. No business Logic. Just a set of containers. Such as
Public Class Contact
    Private _contactId As Integer?
    Private _firstName As String
    Private _lastName As String
    Private _phone As String
    'Plus all the properties to expose these members.
End Class
The second layer is the data access layer. It is responsible for all of the loading and saving objects between the application and database. The client UI layer never touches the data access layer.
Public Class ContactDAL
    'CRUD methods
    Private Sub SelectContact(. . .

    Private Sub InsertContact(. . .

    Private Sub UpdateContact(. . .

    Private Sub DeleteContact(. . .
End Class
The third layer is the Facade. The client UI layer uses the layer exclusively and never calls into any of the other layers. It handles the generation of new objects, retrieving objects, saving objects, and all business logic.
Public Class ContactFacade
    Private Sub GetContact(. . .

    Private Sub SaveContact(. . .

    Private Sub CalculateSales(. . .
End Class 
And finally, the fourth layer is for the UI. The UI layer calls the Facade exclusively for all of it’s business objects needs.
In other tiered approaches it is very common to have the database layer intermingled with the business object layer. That gives you the nice abillilty to simply save an object by calling
   Contact.Save()
However in the BO/DAL/Facade layered approach before VS 2008, you would need to call the fa├žade methods to do your work. For example to save the contact object you would call
ContactFacade.SaveContact(c)
Not horrible but it sure is easier to just call c.Save isn’t it? Well you can with Extension Methods! If you have alread been using this same tiered programming approach, you most likely can just rename your methods and decorate them with the Extension Attribute to immediately be able to use this new functionality.
Function SaveContact(ByVal contact As Contact)
becomes
<Extension()> _
Sub Save(ByVal contact As Contact)
Of course this is assuming your Facade is defined as a module (or static class in C#). Now your UI layer can simply call c.Save()

Note on passing objects ByRef

I did discover a neat effect when working with Extension Methods. Usually a class’s method cannot destroy an object instance. However this can occur with Extension Methods! Some call this a bug, but I call it a feature and it produces the results I would expect.
    <Extension()> _
    Sub Delete(ByRef contact As Contact)
        'Only try to delete from database if object has Database Id
        If contact.ContactId.HasValue Then
            contactDao.DeleteContact(contact)
        End If

        contact = Nothing
    End Sub
Take note that in this example the contact parameter is ByRef. So calling c.Delete() from the UI layer results in c being set to nothing!

Consculsion

So there you have it. It is possible to maintain programming discipline that demands separating business objects and data access, but yet maintain ease of use, thanks to Extension Methods.

No comments: