Wednesday, 15 April 2009

Action<T> and Func<TResult> delegates

In this post I would like to write a little bit about two types of delegates which were added recently with ASP.NET 3.5 (System Namespace):

Action<T> - "Encapsulates a method that takes a single parameter and does not return a value". There are additional versions for two, three and four parameters:
It can be used with method like this:

   1:  public static void UpdatePage(PageData page, Action<PageData> action)
   2:  {
   3:      var writableClone = page.CreateWritableClone();
   4:      action(writableClone);
   5:      DataFactory.Instance.Save(writableClone, SaveAction.Publish);
   6:  }

To updated page in EPiServer it's required to create writable clone first, then we can apply our changes and finally instance of PageData has to be saved. This is a procedure which you can probably find in many places in your projects. With delegates you can easily "close" standard steps into a single method and pass changeable part as a parameter, like this:

   1:  PresenterUtils.UpdatePage(CurrentPage,
   2:                            p =>
   3:                                {
   4:                                    p.Property["MetaAuthor"].Value = "Marek";
   5:                                    p.Property["MainBody"].Value = "Hello World!";
   6:                                });

I think it should be pretty obvious that the above piece of code changes MetaAuthor and MainBody properties on CurrentPage. For me, main benefit of this approach is increased readability, hence the code is easier to maintain. Note that there is nothing in this call about creating writable clone, calling save method on DataFactory, those are all important and required steps to get things working properly but in the same time they are irrelevant from businesses logic point of view. You don't need to see those "standard" actions to understand what your class/method is doing.

You can also use delegates which return a value:
The simples version Func<TResult> - "encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter."

In this example, method returns default value if property doesn't exist or is not set. If property is set then its value can be formatted in some special way:

   1:  public static string GetProperty<T>(PageData page, string propertyName, string defaultValue, Func<T, string> customAction)
   2:  {
   3:      if (page == null || string.IsNullOrEmpty(propertyName) || page[propertyName] == null)
   4:      {
   5:          return defaultValue;
   6:      }
   7:  
   8:      return customAction((T) page[propertyName]);
   9:  }

And this is how this method can be used:

   1:  public string GetFormattedDate(PageData page, string format)
   2:  {
   3:      return PresenterUtils.GetProperty<DateTime>(page, "EventDate", "", x => x.ToString(format));
   4:  }

In the above example we are interested in EventDate property, if this property is not set then empty string (default value) will be returned, otherwise DateTime will be formatted appropriately.

Of course, in pre-ASP.NET 3.5 era things like this were also possible but it was required to define your own delegates etc. Now you can use those which are available for you in the framework. Personally I really like lambda expressions and this "way" of crating APIs, it's much more readable for me therefore I encourage you to give it a try! :)

Friday, 10 April 2009

Convention over Configuration

In this post I would like to introduce design pattern which is particularly close to my heart - Convention over Configuration. What I like the most about this pattern is that it eliminates lots of monkey code which we have to write from time to time. Firstly let me explain to you what I mean by monkey code.

Monkey code

Typical case of monkey code can be found in old-fasihion NHibernate mappings:

   1:  <hibernate-mapping>
   2:    <class name="Product" table="tblProduct">
   3:      <id name="Id" type="Int32" column="ProductID">
   4:        <generator class="identity" />
   5:      </id>
   6:      <property name="ProductLine" type="String">
   7:        <column name="ProductLine" length="2" />
   8:      </property>
   9:      <property name="Class" type="String">
  10:        <column name="Class" length="2" />
  11:      </property>
  12:      <property name="Size" type="String">
  13:        <column name="Size" length="5" />
  14:      </property>
  15:      <property name="DaysToManufacture" type="Int32">
  16:        <column name="DaysToManufacture" not-null="true" />
  17:      </property>
  18:      <property name="ModifiedDate" type="DateTime">
  19:        <column name="ModifiedDate" not-null="true" />
  20:      </property>
  21:      <property name="ListPrice" type="Int32">
  22:        <column name="ListPrice" not-null="true" sql-type="money" />
  23:      </property>
  24:    </class>
  25:  </hibernate-mapping>

Even though in most cases column name and property name are the same, we still have to specify them. There is nothing really creative in this code. I think it's general problem for all sorts of mappings, it doesn't really matter if they are in a form of XML or a plain-old-CLR-objects - someone has to write this pesky piece of code. If you multiply time needed to map single column by number of columns and then by number of tables then you will get huge waste of time.

Conventions

I like Fluent NHibernate so much because it uses conventions, for instance I can say that all my tables have the same names as my classes and let fluent-nh map everything for me.

Within one application there might be number of aspects to which conventions can be applied. For instance, in fluent-nh it might be:

  • class name vs table name
  • property name vs column name
  • reference property name vs foreign key column name

Another project using conventions is ASP.NET MVC:

  • project structure - there are separate folders for Views, Controllers and Model
  • views placement - by default ASP.NET MVC framework will try to find view for given controller and action in folder like this: /Views/controllername/actionname.aspx.
    Action name equals view name.
As you can see based on ASP.NET MVC example, conventions are not just to eliminate monkey code, they can be also used to establish common standards for project structure, classes/properties naming etc.

Implementation

Lets try to put this high level ideas into concrete ... how Convention over Configuration can be implemented? I will try to present an algorithm based on fluent-nh and real life scenario where we have to map database tables to classes but all tables in database have "tbl" prefix. Of course we don't want to name our classes with this prefix, also we don't want to specify manually for each class that corresponding table's name is "tbl" + class name.

Normally with fluent-nh you would write your own convention implementing IClassConvention interface, then you would use FluentConfiguration class to "fluently" configure your database, add your mappings and also add your custom conventions. Having that you would call BuildSessionFactory() to get instance of properly configured NHibernate's SessionFactory. It's all straightforward but lets look under the hood to see what is going on behind the scene:
  1. In the first step fluent-nh loads classes responsible for "convention discovery". Each class from this group is responsible for single convention, this way it's really simple to add new additional conventions later.
    To find all "discovery" classes you can for instance use reflection to get all classes from a given namespace (flunet-nh's way) or all classes implementing some interface.
  2. Then it instantiates all mapping classes, custom conventions and default conventions. Default conventions will be used if there are no custom conventions. Be default class name will be equal to table name, we want to change it therefore we have to provide our own convention. It's important to remember that default convention shouldn't overwrite custom one.
  3. Next, it executes all "convention discovery" classes. Corresponding methods will get as a parameter all mappings and are responsible for finding and applying specific conventions.
This is high level algorithm which allows you easily add additional conventions. You can have set of default conventions, but also you can provide your custom one. In general it is important to remember that
  1. Any convention (default or custom) shouldn't overwrite explicit configuration!
  2. Default convention shouldn't overwrite custom conventions!

Code samples

All conventions have to implement this simple interface:


   1:  public interface IConvention<T> : IConvention
   2:  {
   3:      /// <summary>
   4:      /// Whether this convention will be applied to the target.
   5:      /// </summary>
   6:      /// <param name="target">Instace that could be supplied</param>
   7:      /// <returns>Apply on this target?</returns>
   8:      bool Accept(T target);
   9:  
  10:      /// <summary>
  11:      /// Apply changes to the target
  12:      /// </summary>
  13:      /// <param name="target">Instance to apply changes to</param>
  14:      void Apply(T target);
  15:  }

Accept() method allows you to have number of conventions of given type and use them based on your custom logic.

This is how "discovery" is implemented:

   1:  public void Apply(IEnumerable<IClassMap> classes)
   2:  {
   3:      var conventions = conventionFinder.Find<IClassConvention>();
   4:  
   5:      foreach (var classMap in classes)
   6:      {
   7:          foreach (var classConvention in conventions)
   8:          {
   9:              if (classConvention.Accept(classMap))
  10:                  classConvention.Apply(classMap);
  11:          }
  12:      }
  13:  }

ConventionFinder returns all objects implementing IClassConvention interface, then it applies this all conventions to all mappings.

Finally, our convention which will add "tbl" prefix for table name:


   1:  public class TableNameConvention : IClassConvention
   2:  {
   3:      public bool Accept(IClassMap target)
   4:      {
   5:          return string.IsNullOrEmpty(target.TableName);
   6:      }
   7:  
   8:      public void Apply(IClassMap target)
   9:      {
  10:          target.WithTable("tbl" + target.EntityType.Name);
  11:      }
  12:  }

And a few interesting pieces of code which I found in fluent-nh sources:


   1:  private void AddDefaultConventions()
   2:  {
   3:      foreach (var foundType in from type in typeof(PersistenceModel).Assembly.GetTypes()
   4:                                where type.Namespace == typeof(TableNameConvention).Namespace && !type.IsAbstract
   5:                                select type)
   6:      {
   7:          ConventionFinder.Add(foundType);
   8:      }
   9:  }

The above method finds all default conventions. Next method instantiates type using default constructor or the one which takes (in this case) ConventionFinder as a parameter:

   1:  private object Instantiate(Type type)
   2:  {
   3:      object instance = null;
   4:  
   5:      foreach (var constructor in type.GetConstructors())
   6:      {
   7:          if (IsFinderConstructor(constructor))
   8:              instance = constructor.Invoke(new[] { this });
   9:          else if (IsParameterlessConstructor(constructor))
  10:              instance = constructor.Invoke(new object[] { });
  11:      }
  12:  
  13:      return instance;
  14:  } 

I hope that this post gives you much better idea about Convention over Configuration in general but also explains how to implement this pattern in practice. I encourage you to give it a try instead of writing monkey code.

If you encountered different cases where CoC fits perfectly I would be very interested to hear about it, leave a comment or send me an email. Or maybe you in general disagree ... leave a comment ... it will be interesting to know your view on this.

Wednesday, 1 April 2009

Conventions - After Rewrite

In December I have written a post about Conventions and AutoPersistenceModel in Fluent NHibernate. Since then lots of things have changed, especially with conventions, in this post I would like to show how to accomplish the same, old goals with new API.

To recap quickly, we have two following tables:

The issues which we are going to solve with conventions include:
AdventureWorks database doesn't follow the default convention. Differences:
  • table name is different then class name (Product vs Production.Product)
  • id property has different name then primary key column (Id vs ProductID)
  • properties representing links between tables have different names then foreign key column names (Product vs ProductID)

Here is how we can create conventions which are specific for AdventureWorks database:

IClassConvention

By default class name equals table name. If for some reason it's not true in your case then basically you have two options:
  • in each mapping class you can call WithTable(...) method and pass as a parameter your custom table name 
  • create your own convention by implementing IClassConvention
Of course second option is preferable in most cases, IClassConvention interface has to methods which need to be implemented:

   1:  public bool Accept(IClassMap target)
   2:  {
   3:      // apply this convention if table wasn't specified with WithTable(..) method
   4:      return string.IsNullOrEmpty(target.TableName);
   5:  }
   6:   
   7:  public void Apply(IClassMap classMap)
   8:  {
   9:      // table name can be like: 
  10:      // "Production." + class name  or  "Sales." + class name
  11:      // "Production" and "Sales" are the last parts of the namespace 
  12:   
  13:      var lastElementOfNamespace = classMap.EntityType.Namespace
  14:          .Substring(classMap.EntityType.Namespace.LastIndexOf('.') + 1);
  15:   
  16:      classMap.WithTable(String.Format("{0}.{1}", lastElementOfNamespace, classMap.EntityType.Name));
  17:  }

I think comments explain everything but I would like to stress the way Accept() method was implemented in. It's important to remeber that we don't always want to apply our conventions! If, for some reason, in mapping class WithTable(...) method was called to set specific table name then we shouldn't overwrite it. 

IIdConvention

Default convention for identity properties is also fairly straightforward - property name equals column name. If you need to change it then again there are two options:
  • in mapping class you can specify column name like this: Id(x => x.Id, "ProductID"); 
  • or implement IIdConvention and provide your own convention

   1:  public bool Accept(IIdentityPart target)
   2:  {
   3:      // make sure that column name wasn't set by calling Id(x => x.Id, "...")
   4:      return string.IsNullOrEmpty(target.GetColumnName());
   5:  }
   6:   
   7:  public void Apply(IIdentityPart target)
   8:  {
   9:      // primary key = class name + "ID"
  10:      target.ColumnName(target.EntityType.Name + "ID");
  11:  }
A few words of explanation, in database, primary keys are named like ProductID, ProductReviewID whereas in our class I want to stay with simple Id property for each class. Hence, to build column name it's required to use class name (for instance Product) and add "ID".

IReferenceConvention and IHasManyConvention

Last issue to deal with are foreign keys. ProductReview table has foreign key to Product table, as usually, property name (Product) is different then column name (ProductID) and therefore instead of specifing that (References(x => x.Product, "ProductID");) we can implement IReferenceConvention interface:


   1:  public bool Accept(IManyToOnePart target)
   2:  {
   3:      // make sure that column name wasn't set with References(x => x.Product, "...");
   4:      return string.IsNullOrEmpty(target.GetColumnName());
   5:  }
   6:   
   7:  public void Apply(IManyToOnePart target)
   8:  {
   9:      // foreign key column name = property name + "ID"
  10:      //
  11:      // it will be used in case like this:
  12:      // <many-to-one name="Product" column="ProductID" />
  13:   
  14:      target.ColumnName(target.Property.Name + "ID");
  15:  }

And to map easily other side of this association, without spcifying key column names we have to implement IHasManyConvention:

   1:  public bool Accept(IOneToManyPart target)
   2:  {
   3:      return target.KeyColumnNames.List().Count == 0;
   4:  }
   5:   
   6:  public void Apply(IOneToManyPart target)
   7:  {
   8:      // foreign key column name = class name + "ID" 
   9:      //
  10:      // it will be used to set key column in example like this:
  11:      //

  12:      // <bag name="ProductReview" inverse="true">
  13:      //   <key column="ProductID" />
  14:      //   <one-to-many class="AdventureWorksPlayground...ProductReview, AdventureWorksPlayground, ..." />
  15:      // </bag>
  16:   
  17:      target.KeyColumnNames.Add(target.EntityType.Name + "ID");
  18:      target.LazyLoad();
  19:      target.Inverse();
  20:  }

Please note that in last example we are not only setting key column name but also some other parameters like lazy load and inverse. 

Last words

As you can see with new API you need to write slightly more lines of code to achieve the same effect but I think it's a fair trade off for much greater control and flexibility. 

This post is just "touching" the subject, you can find much more about conventions and different interfaces on wiki. 

Related posts: