August 22, 2007

LINQ to SQL: Extension Methods

I've talked about Extension Methods (I probably called them partial methods) before. Now that I actually have Linq to SQL in my hands I thought I would take a look at what extension methods are created when you start with LINQ to SQL.

In my previous example, I showed a sample database that looked like this:


My designer is named "LinqTests.dbml", which is an xml file that is used to generate LinqTests.designer.cs. That is where we find all of the code.

First, we will look at the classes that map to the tables. We have a Customer, Product, and CustomerProduct tables. In each class there is a region named "Extensibility Method Definitions", that is where we are looking right now.

For the Customer class, this is what we see:


Basically, that accounts for two extension methods for every column in a table, plus three more: OnLoaded(), OnValidate(), and OnCreated().

If you can't tell from the name: OnCreated is called right as a new Customer object is created, the OnLoaded() is called after data is loaded into it by Linq to Sql, and OnValidate is called before the code is saved (I think -- since I can't find the code that actually calls it).

The others are for before and after a particular property is set.

Looking into the code further you can see how the code is being called. Here is the constructor:

public Customer()
{
    OnCreated();
    this._CustomerProducts = new EntitySet<CustomerProduct>(
new
Action<CustomerProduct>(this.attach_CustomerProducts),
new Action<CustomerProduct>(this.detach_CustomerProducts));
}

The line OnCreated(); calls the OnCreated method (duh!), but if you search the code you wont find anywhere that OnCreated does anything. That is for you to do in a partial class.

Now, to add some business logic to my Customer class for when the class is created or loaded, the code will look like this:



public partial class Customer
{
    partial void OnCreated()
    {
        this.Name = "Billy-Mac";
    }
    partial void OnLoaded()
    {
        if (string.Empty( this.Name ))
            this.Name = "Jim-Bob";
    }
}


Back to basics here. The generated DataContext class also has some Extension Methods



So now you have three extension methods for each table that the Data Context has to manage. One for Insert, Update, and Delete; plus one more for when the Data Context class is created.


Lets take a step back now. Does this really get us anywhere? Before, with generated domain objects (those are the objects that are created to hold data) you could still accomplish all of this using inheritance. The problem with using inheritance is that now you have to use the inherited class and shouldn't user the base class. Worse, there really isn't a good way to tell people not to use the base classes.

Now with extension methods you will have less need to inherit your base domain objects. Now you have the hooks you need to extend your domain objects to fit your business needs. Your developers (or just yourself) now have one set of objects to remember to use. Kind of like one stop shopping.

I've still heard a bit of moaning and groaning about extension methods on the web -- I don't agree with them. I think extension methods are a wonderful addition to C# that will really help fix a need. And what Linq for SQL has done has only helped to illustrate that need for everyone.

2 comments:

Grumpy Grandma said...

Hey dude,

Just FYI, those methods marked with partial are called "partial methods", not extension methods

http://blogs.msdn.com/wesdyer/archive/2007/05/23/in-case-you-haven-t-heard.aspx

Anonymous said...

@Grumpy Grandma: Take your Metamucil...it's just terminology! A LINQ to SQL Extensibility Method "is a" partial method...mmmkay?