Fluent Silverlight – Implementing a fluent API
Please view the table of content of this series for reference.
Introduction
Quite some time has passed since we published our Fluent Silverlight framework. In the mean time we have been very busy extending the framework and using it in our internal products. Unfortunately we have been so busy that there was no time left to publish the extension we made.
Never the less there has been some interest in this framework lately such as that I decided to finally take a spin an bring the current version that we use internally in our company to a level where I can publish it. The updated framework will be published shortly.
Since code alone does not always show the possibilities how to use it I want to second it with a series of short articles.
A fluent API
A good description on what a fluent API is can be found here. Generally spoken a fluent API makes the code more readable.
In our context we can identify four different categories that we want to define via the fluent interface.
- basic property values like strings, numbers, boolean values, dates, etc.
- enum type values like Visibility, HorizontalAlignment, BindingMode, etc.
- binding dependency properties to properties of the view model
- binding events to commands on the view model
Let me show a simple sample of a fluent API which incorporates the first two categories mentioned above.
Car car = new CarExpression()
.Make("Kia Sorento")
.ListPrice(22500)
.Type.SUV()
.Build();</p>
First I create an instance of type CarExpression. Then I define the make and list price of the car which are standard properties of type string and decimal respectively. Next I define the type of the car which is an enum. Finally I tell the CarExpression to build the car with the given settings. The definition of the Car class is just
public class Car
{
public string Make { get; set; }
public decimal ListPrice { get; set; }
public CarTypes Type { get; set; }
}</p>
and the CarTypes enum is defined as follows
public enum CarTypes
{
SportsCar, SUV, MiniVan, Sedan
}</p>
having defined these two types I can then start implementing the CarExpression as shown below
public class CarExpression
{
private string make;
private decimal listPrice;
private CarTypes carType;
public CarExpression Make(string value)
{
make = value;
return this;
}
public CarExpression ListPrice(decimal value)
{
listPrice = value;
return this;
}
}</p>
We can see that for each property of the car that I want to define via the fluent API I have defined an internal field in the CarExpression. For normal properties like Make the definition of the corresponding method in the fluent API is simple. It’s always a method which returns an object of type CarExpression. Thus we will always have a return this; statement at the end of the method. Other than that the method just stores the passed in parameter value in the corresponding internal field for future use.
It is the fact that the methods are always returning “this” makes it possible to have a fluent interface.
Enum type properties are a little bit more involved. We have to implement an expression on its own for each enum we want to use in our fluent API. Let me show the code used for the CarTypes enum
public class CarTypeExpression<TParent>
{
private readonly TParent parentExpression;
private readonly Action<CarTypes> action;
public CarTypeExpression(TParent parentExpression, Action<CarTypes> action)
{
this.parentExpression = parentExpression;
this.action = action;
}
public TParent SportsCar()
{
action(CarTypes.SportsCar);
return parentExpression;
}
public TParent SUV()
{
action(CarTypes.SUV);
return parentExpression;
}
public TParent Sedan()
{
action(CarTypes.Sedan);
return parentExpression;
}
public TParent MiniVan()
{
action(CarTypes.MiniVan);
return parentExpression;
}
}</p>
The points to mention are that the expression is generic. The generic parameter TParent will correspond to the expression in which context this enum expression is going to be used. In our simple sample this will be the CarExpression. The other point to mention is the constructor. The constructor expects 2 parameters, the first being the parent expression inside which this enum expression is used and the second parameter is the action that shall be executed by this enum expression whenever one of its methods is used. The enum expression is only a helper expression and thus must not store any values internally but rather pass selected values back to the parent expression. That is the reason why we have to define this action.
Now let’s have a look a one of those methods of the enum expression. In the first line we use the action mentioned above and pass as a parameter the correct enum value. Then we return the parent expression instance. This is very important! We want to continue working with the car expression once we have selected a car type. If we would instead return “this” then we could only select other types which makes absolutely no sense…
The reason why we have chosen the enum expression to be generic in TParent is that we might want to use an enum expression in different contexts, that is inside different parent expressions. If we are 100% sure that we are only going to use an enum expression inside a single type of expression then we could simplify the above enum expression and make it non-generic.
So let’s now show the missing parts of the car expression
public class CarExpression
{
...
public CarTypeExpression<CarExpression> Type
{
get{ return new CarTypeExpression<CarExpression>(this, x => carType = x);}
}
public Car Build()
{
return new Car {Make = make, ListPrice = listPrice, Type = carType};
}
}</p>
As you will notice we have implemented the Type as a readonly property and not as a method. Please also note the lamda expression which defines the action we pass to the enum expression
x => carType = x</p>
This lambda expression tells the system to take the parameter that the respective method in the enum expression will pass to the action and assign its value to the field carType of the car expression.
What I do not like in my fluent API that I have defined so far is the fact that I have to call a Build method to tell the expression to build an instance of a car. That seems something unnecessary to me… How can I let the expression know that I want to have my car built without explicitly telling it? Well if I don’t want to tell it explicitly then I have to tell it implicitly. What means do I have to tell something implicitly? Oh yes, i can use operator overloading to define an implicit type cast operation.
Whenever I assign an object of type CarExpression to a variable of type Car the system tries to do an implicit type conversion. If no such conversion is defined the system will throw a TypeCastException. But we can define such an implicit type conversion. In our sample the code needed looks like this
public static implicit operator Car(CarExpression expression)
{
return new Car
{
Make = expression.make,
ListPrice = expression.listPrice,
Type = expression.carType
};
}</p>
We can now update our usage of the expression like shown below
Car car = new CarExpression()
.Make("Kia Sorento")
.ListPrice(22500)
.Type.SUV();</p>
This is much more natural than explicitly calling some Build method.