Fluent interface and method chaining in C# for beginners

As a C# developer you expose functionality using classes and objects. Many a
times the code required to accomplish a task using these classes and objects
tends to be complex. In an attempt to reduce this complexity you might want to
device a technique that makes your code more readable and natural. That is where
Fluent Interface and
Method
Chaining can be useful. The remainder of this article discusses these
techniques with simple examples. My aim is to quickly introduce you to these
concepts. So, the examples are kept simple rather than building a full-fledge
example.
Fluent interface and method chaining
Object oriented systems often involve a lot of classes and objects. To
accomplish a specific task these classes and objects are utilized in a certain
way. At times this consumption of classes and objects introduces complexity.
Fluent interface is a technique that helps you reduce this complexity by
creating a "domain specific language" consisting of several methods. Fluent
interface also makes your code more readable and natural because it uses method
chaining.
Method chaining is a technique where one method is called directly on another
method forming a chain like structure. Consider the following example.
var emp = new Employee()
.SetBasicDetails(1,"Nancy","Davolio")
.SetSalaryDetails(20000)
.SetProjectDetails("PROJ1",DateTime.Now);
Suppose you have the Employee class with three methods namely SetBasicDetails(),
SetSalaryDetails(), and SetProjectDetails(). The above code creates an Employee
object using the new keyworkd of C#. Then SetBasicDetails() method is called on
that object in order to set basic details such as employee ID, first name, and
last name. The SetBasicDetails() method is supposed to be returning an Employee
object. The second method - SetSalaryDetails() - is directly called on this
returned object thus forming a chain. On the same lines SetSalaryDetails() is
supposed to return the Employee object and SetProjectDetails() method is invoked
on it thus continuing the chain.
Note that the fluent interface and method chaining are not the same things.
Fluent interface is a concept and method chaining is a way to implement it. In
the above example, SetBasicDetails(), SetSalaryDetails(), and SetProjectDetails()
form the domain specific language.
Method chaining simply means that the return value from one method is used to
invoke the next method in the chain. Fluent interface requires that the methods
participating in the chain return the same object (or context) to the next
method in the chain.
If you have worked with LINQ, EF Core Fluent API, or ASP.NET Core's Startup
class, chances are you have already used method chaining. Consider the following
LINQ code:
List<Employee> list = new List<Employee>();
...
...
var data = list.Where(e => e.Country == "USA")
.OrderBy(e => e.EmployeeID)
.ToList();
The above code creates a List of Employee objects and stores certain
employees (not shown in the code) in it. Then a LINQ query picks employees
matching certain condition. Notice how various aspects of the LINQ query such as
a WHERE condition and an ORDER BY clause are handled using method chaining. The
terminating method ToList() ends the chain by returning the final list of
employees (after applying the previous conditions).
The following example shows ConfigureServices() method from ASP.NET Core
Startup class.
services.AddRazorPages()
.AddRazorPagesOptions(...);
As you can see, AddRazorPages() method returns IMvcBuilder. So, the same
context is passed to the AddRazorPagesOptions() method chained to it. The
AddRazorPagesOptions() method also returns IMvcBuilder so that further methods
in the chain (if any) can use it.
Finally, consider the following EF Core Fluent API code fragment.
modelBuilder.Entity()
.Property(e => e.EmployeeID)
.HasColumnName("EmployeeID")
.HasDefaultValue(0)
.IsRequired();
Here, Property(), HasColumnName(), HasDefaultValue(), and IsRequired() form
the method chain and the same context is passed between the calls.
Now that you have some idea about fluent interface and method chaining, let's
see a few simple examples of implementing them in C# code.
Example 1 - Performing CRUD operations
Suppose that you want to perform CRUD operations on a table. To accomplish
this task you can create a class and the required methods. Consider the
following code that shows one such implementation.
var crud = new CrudOnTable("Employees");
crud.Insert("Nancy Davolio");
crud.Update(1, "Andrew Fuller");
crud.Delete(3);
crud.Select(1, out data);
This code doesn't use method chaining and invokes Insert(), Update(),
Delete(), and Select() methods as independent lines of code.
If you want to support method chaining you need to design your CrudOnTable
class in a specific way. Let's see how that can be done.
public class CrudOnTable
{
public CrudOnTable(string table)
{
...
}
public CrudOnTable Insert(string data)
{
...
return this;
}
public CrudOnTable Update(int id, string data)
{
...
return this;
}
public CrudOnTable Delete(int id)
{
...
return this;
}
public CrudOnTable Select(int id, out string data)
{
...
return this;
}
}
The above code shows the CrudOnTable class with a constructor and four
methods.
The constructor accepts a name of the table on which CRUD operations are to
be performed. The above code doesn't store this name anywhere but in a realistic
implementation you will store it in some variable or auxiliary object.
Notice all the other methods such as Insert() and Update(). Although they
lack full-fledge implementation, the important part is in place. As you can see,
all of them return "this". That means they do insert, update, delete, or select
operation and return the same context (the CurdOnTable object).
Now you can call the methods using chaining syntax as shown below:
var crud = new CrudOnTable("Employees")
.Insert("Nancy Davolio")
.Update(1, "Andrew Fuller")
.Delete(3)
.Select(1, out data);
Since all the methods return the same CrudOnTable, you can chain them as
shown above. The resultant code is tidy and more readable than the earlier
fragment.
The CrudOnTable object returned from the Select() method is assigned to the
crud variable and it will have the final state after executing all the earlier
methods in the chain.
Example 2 - Processing an order
In the previous example all the methods of the chain were independent of one
another. You could have even omitted calling all the methods. For example, you
could have used only Insert() and Delete() methods. However, at times you need
to device methods that are related to a task. Upon executing the complete chain
the final result is obtained.
Suppose you are building an order processing system. You might create a set
of methods to complete the task of processing the order. These methods are shown
below:
var order = new Order()
.AddCustomerDetails("Customer 1")
.AddItem("ABCD", 10)
.AddItem("PQRS", 20)
.AddShippingDetails("address","city","postalcode")
.Process();
As you can see, the code creates a new Order object. Then a set of chained
methods is called on the object. The AddCustomerDetails() is supposed to add
customer details for the order. One or more AddItem() calls add order items. The
AddShippingDetails() method fills the shipping details for the order. Finally,
the Process() method places the order in the system and does the required
processing.
So, in this case you use these methods as one set. And they are required to
successfully complete the task. The Order class used by the above code fragment
is shown below:
public class Order
{
public Order AddCustomerDetails(string name)
{
return this;
}
public Order AddItem(string code, int qty)
{
return this;
}
public Order AddShippingDetails(string address,
string city,
string postalcode)
{
return this;
}
public Order Process()
{
return this;
}
}
In this case the Process() method acts as a end method or terminating method
that finishes the task and returns the final Order object.
Example 3 - Enforcing a sequence of operations
In the preceding example you created a set of methods to complete the order
processing task. Although this might work as expected, it has its own problem.
These methods are supposed to be called in a specific order. And currently there
is no way to enforce that. For example, consider the following code fragment :
var order = new Order()
.AddCustomerDetails("Customer 1")
.AddShippingDetails("address","city","postalcode")
.Process();
This code doesn't call AddItem() method at all. So, the Process() method is
not going to work as expected. Similarly, you may want that AddCustomerDetails()
must be called prior to calling any other method in the chain.
To enforce such a sequence you can resort to interfaces. To give you some
idea let's see how interfaces can be used in such cases.
Consider the following interfaces:
public interface IAddOrderItem
{
IAddOrShipItem AddItem(string code, int qty);
}
public interface IAddOrShipItem
{
IAddOrShipItem AddItem(string code, int qty);
IReadyToProcess AddShippingDetails(string address,
string city,
string postalcode);
}
public interface IReadyToProcess
{
Order Process();
}
The IAddOrderItem interface contains AddItem() method. The AddItem() method
returns IAddOrShipItem object. The IAddOrShipItem interface contains AddItem()
and AddShippingDetails() methods. The AddShippingDetails() method returns
IReadyToProcess object. The IReadyToProcess interface contains Process() method.
And the Process() method returns Order object.
Once these interfaces are ready, you will implement all of them on the Order
class:
public class Order:IAddOrderItem, IAddOrShipItem,
IReadyToProcess
{
...
}
You will ensure that initially AddCustomerDetails() should be called. To
enforce this rule you need to make the class constructor private as shown below:
private Order() { }
Since the Order class implements IAddOrderItem interface, the implementation
of AddCustomerDetails() will be as follows:
public static IAddOrderItem AddCustomerDetails
(string name)
{
return new Order();
}
As you can see, AddCustomerDetails() is a static method and creates a new
Order object. This context will flow through the rest of the methods in the
chain.
The implementation of AddItem() will now look like this:
public IAddOrShipItem AddItem(string code, int qty)
{
return this;
}
Notice that since Order class implements IAddOrShipItem interface, AddItem()
can return "this".
On the same lines, implementation of AddShippingDetails() will look like
this:
public IReadyToProcess AddShippingDetails(string address,
string city,
string postalcode)
{
return this;
}
Finally, the Process() method will look like this:
public Order Process()
{
return this;
}
If you try to use the Order class, you will be required to call the
AddCustomerDetails() first because that's what will be available to you:

Once you call AddCustomerDetails(), you can then invoke AddItem() method one
or more times.

Upon calling AddItem() one or more times, you can call AddShippingDetails().

Finally, you can terminate the chain by calling the Process() method.

You might have guessed that enforcing such constraints requires thoughtful
designing of the system because there could be many permutations and
combinations of the method chains.
That's it for now! Keep coding!!