Los Techies : Blogs about software and anything tech!

Fluent Silverlight – Fluent API and inheritance


Please view the table of content of this series for reference.

Introduction

In my last post I showed how we  can implement a fluent API to help us construct instances of specific classes. When using a fluent API the code is in general much more readable. In this post I want to go one step further and show how we can implement a hierarchical fluent API.

Defining a base expression

Our objects that we use in our applications are often inherited from some base class as in the following simple example.

image

Here we have an abstract Person base class from which the Employee class inherits. We might have another class e.g. Customer that also inherits from Person. If we now want to implement a fluent API for the construction of Employee- and/or Customer objects then it makes sense that our fluent API is also hierarchically organized. We might want to build a parallel hierarchy of PersonExpression, EmployeeExpression and CustomerExpression where the latter two inherit from the first expression.

But now we have a problem. From my previous article we know that each method in a fluent API has to return the expression itself. But if we now implement say the LastName() method in the PersonExpression like this

public abstract class PersonExpression
{
    private string lastName;
    ...
 
    public PersonExpression LastName(string value)
    {
        lastName = value;
        return thisInstance();
    }
 
    ...
}

then when used in the context of an EmployeeExpression (where EmployeeExpression inherits from PersonExpression)

Employee john = new EmployeeExpression()
    .FirstName("John")
    .LastName("Doe")
    ...

we will not be able to get to any of the members defined in the EmployeeExpression any more since our continuation is of type PersonExpression and NOT of type EmployeeExpression. With other words: whenever we use a method of the base expression in our fluent expression then we loose the context!

Thus we have to find another solution. The solution is to introduce a generic parameter which will be used as the type of the return value of the methods of our expression classes. Consequently we will write

public abstract class PersonExpression<THIS>
    where THIS : PersonExpression<THIS>
{
    private string lastName;
    ...
 
    public THIS LastName(string value)
    {
        lastName = value;
        return thisInstance();
    }
 
    ...
}

If we use the generic parameter THIS instead of a concrete (base) expression, then we will never loose the context. Please note also that the LastName method does not return ‘this’ any more but rather the return value of the function thisInstance which is defined as follows

protected THIS thisInstance()
{
    return (THIS)this;
}

So we never should return directly this any more in our expression hierarchy but rather thisInstance().

But wait, we have still one more problem: since we want to define the implicit type conversion in the base class we also have to introduce the target type as a generic parameter such as that we can then write

public static implicit operator TARGET(PersonExpression<THIS, TARGET> expression)
{
    var target = new TARGET();
    expression.BuildUp(target);
    return target;
}

where the BuildUp function is defined as follows

protected virtual void BuildUp(TARGET target)
{
    ...
}

this method will be overriden by the child expressions to build-up the target object.

The full code for the PersonExpression will now look like this

public abstract class PersonExpression<THIS, TARGET>
    where THIS : PersonExpression<THIS, TARGET>
    where TARGET : Person, new()
{
    private string lastName;
    private string firstName;
    private DateTime dateOfBirth;
    private Gender gender;
 
    public THIS LastName(string value)
    {
        lastName = value;
        return thisInstance();
    }
 
    public THIS FirstName(string value)
    {
        firstName = value;
        return thisInstance();
    }
 
    public THIS DateOfBirth(DateTime value)
    {
        dateOfBirth = value;
        return thisInstance();
    }
 
    public GenderExpression<PersonExpression<THIS, TARGET>> Gender
    {
        get{ return new GenderExpression<PersonExpression<THIS, TARGET>>(this, x => gender = x);}
    }
 
    protected THIS thisInstance()
    {
        return (THIS)this;
    }
 
    protected virtual void BuildUp(TARGET target)
    {
        target.LastName = lastName;
        target.FirstName = firstName;
        target.DateOfBirth = dateOfBirth;
        target.Gender = gender;
    }
 
    public static implicit operator TARGET(PersonExpression<THIS, TARGET> expression)
    {
        var target = new TARGET();
        expression.BuildUp(target);
        return target;
    }
}

Note how the two generic parameters are restricted by their respective where filters. This restriction is very important to make the whole concept work.

The full code for the EmployeeExpression is given below

public class EmployeeExpression : PersonExpression<EmployeeExpression, Employee>
{
    private DateTime hireDate;
    private decimal yearlySalary;
 
    public EmployeeExpression HireDate(DateTime value)
    {
        hireDate = value;
        return thisInstance();
    }
 
    public EmployeeExpression YearlySalary(decimal value)
    {
        yearlySalary = value;
        return thisInstance();
    }
 
    protected override void BuildUp(Employee target)
    {
        base.BuildUp(target);
        target.HireDate = hireDate;
        target.YearlySalary = yearlySalary;
    }
}

and the expression can now be used in the following way

Employee john = new EmployeeExpression()
    .FirstName("John")
    .LastName("Doe")
    .DateOfBirth(new DateTime(1964, 4, 14))
    .HireDate(new DateTime(2007, 3, 1))
    .YearlySalary(90000);

With the aid of some “generics magic” we are now able to implement a hierarchically structured fluent API for our objects we want to construct. This is a very important pattern we used when implementing Fluent Silverlight.

Kick It on DotNetKicks.com
Posted Jan 03 2010, 11:09 PM by Gabriel N. Schenker

Comments

Fluent Silverlight ??? Fluent API and inheritance - Gabriel … Silverlight Web wrote Fluent Silverlight ??? Fluent API and inheritance - Gabriel &#8230; Silverlight Web
on 01-03-2010 10:49 PM

Pingback from  Fluent Silverlight ??? Fluent API and inheritance - Gabriel … Silverlight Web

Bjoern Rochel wrote re: Fluent Silverlight – Fluent API and inheritance
on 01-04-2010 1:10 AM

Thanks for posting this. I stumbled onto the "this problem" when using inheritance with fluent apis several times in the past, but never found a solution I was 100% satisfied with. What I a difference a little method can make (We were missing the trick with the generic THIS ;-().

Twitter Trackbacks for Fluent Silverlight ??? Fluent API and inheritance - Gabriel Schenker's Blog - Los Techies : Blogs about [lostechies.com] on Topsy.com wrote Twitter Trackbacks for Fluent Silverlight ??? Fluent API and inheritance - Gabriel Schenker's Blog - Los Techies : Blogs about [lostechies.com] on Topsy.com
on 01-04-2010 1:15 AM

Pingback from  Twitter Trackbacks for                 Fluent Silverlight ??? Fluent API and inheritance - Gabriel Schenker's Blog - Los Techies : Blogs about         [lostechies.com]        on Topsy.com

The Morning Brew - Chris Alcock » The Morning Brew #509 wrote The Morning Brew - Chris Alcock &raquo; The Morning Brew #509
on 01-04-2010 3:13 AM

Pingback from  The Morning Brew - Chris Alcock  » The Morning Brew #509

Sanjeev Agarwal wrote Daily tech links for .net and related technologies - Jan 4-6, 2010
on 01-05-2010 1:12 AM

Daily tech links for .net and related technologies - Jan 4-6, 2010 Web Development Thoughts on ASP.NET

Gabriel Schenker's Blog wrote Fluent Silverlight – Binding dependency properties to model properties
on 01-07-2010 8:47 AM

In my previous posts ( here and here ) I discussed how one can build a fluent API for the definition

Gabriel Schenker's Blog wrote Fluent Silverlight – Table of content
on 01-08-2010 8:54 AM

In a series of posts I would like to introduce our Fluent Silverlight framework and discuss the important

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems