Thursday, December 6, 2007

Extension Powers for Good instead of Evil

There's been quite a bit of discussions lately on a variety of mailing lists that I subscribe to about the power behind the Extension Methods functionality that is present within the C# 3.0 and VB.NET 9.0 compilers.

While I definitely agree that Extension Methods could potentially be abused, they may also provide a good service as well, which I will describe with an example.

I use the Castle Project components, for instance Windsor and ActiveRecord.  However, to keep functionality separate and distinct, instead of inheriting from ActiveRecordBase<> and allowing everyone able to see the domain model access to use the data services, instead I implement a repository service.  Besides limiting the functionality of accessing the repository only to the tiers of the application that need the access, this also allows me to not require the Castle.ActiveRecord dependency throughout the program, and I can abstract the concrete class and limit the exposure of Castle.ActiveRecord to only one assembly.

However, this technique also leaves some horrid syntax compared to the ActiveRecordBase<> implementation:

Product[] items = Product.FindAll();

versus

Product[] items = container.GetService<IRepository<Product>>().FindAll();

And it gets worse if you do anything fancy, like for example I like to wrap transactions within my repository:

using IRepository<Product> repo = container.GetService<IRepository<Product>>();

I also like (whether it's proper or not is left to the reader), to create a "core" of my application for the logic layers, and with a base static class, such as "App".  This App class houses my windsor container (allowing me to contain the exposure of Castle.Windsor and Castle.MicroKernel as well).  Within this class I separate functionality into sub properties, such as a Model property of type "ModelServices" for model services:

App.Model.NewRepository<Product>();

which changes the code above:

Product[] items = App.Model.NewRepository<Product()>().FindAll();

While this is a little cleaner, in my opinion anyway, it still is very involved when it comes to reading code.

Extension methods can actually help here, at least to some extent.  As an example, if I were to create a new extension method, that is in the same namespace as the Product class, such as:

public static class ProductExtensions

{

public static IRepository<Product> Product(this ModelServices target)

{

return target.NewRepository<Product>();

}

Now with this extension, any code which pulls from the same namespace as where the Product class is can clean up a little bit:

Product[] items = App.Model.Product().FindAll();

I like this because it allows me to bring in new pieces into my services in a cleaner manner than having to request GetService, while at the same time keeping things separated and loosely coupled.

Any comments or feedback on this tactic would be appreciated :).