How to use fluent interfaces and method chaining in C#

By FoxLearn 1/6/2025 4:04:56 AM   65
Fluent interfaces and method chaining are techniques used in programming to improve code readability and simplicity.

Fluent interfaces allow for a more natural, readable flow of code by enabling methods to be called in a sequence, while method chaining allows multiple methods to be invoked on the same object in a single line.

Understanding Fluent Interfaces and Method Chaining

  • Method chaining is a technique where methods are called in sequence, each returning an instance of a class, allowing them to be chained into a single statement.
  • A fluent interface is an object-oriented API that relies on method chaining to reduce code complexity, improve readability, and create a domain-specific language (DSL).

Here's an example of how methods can be chained together:

var results = products.Where(p => p.Price > 100)
                      .OrderBy(p => p.Name)
                      .Select(p => p.Name)
                      .ToList();

In method chaining, each method call passes its result to the next method in the sequence, allowing them to be linked together. This flow of context between methods is why it's called "chaining."

Example of Method Chaining in C#

Here's an example of method chaining in action:

public class Program
{
    public static void Main(string[] args)
    {
        var logger = new LoggerConfiguration()
                     .WriteTo.Console()
                     .WriteTo.File("logs.txt")
                     .CreateLogger();
                     
        logger.Information("Logging started.");
    }
}

In this example, methods are chained together to configure the logger, allowing for a clear and concise setup in a single statement.

Fluent interfaces vs method chaining

Fluent interfaces and method chaining share similarities but also have key differences.

  • Fluent interfaces typically operate on the same set of data and return an instance of the same type, while method chaining is used to modify aspects of a more complex object and may return instances of different classes.
  • Fluent interfaces are built using method chaining, but not all method chaining implementations qualify as fluent interfaces.
  • Fluent interfaces are often used to work with complex objects, while method chaining generally handles simpler data.

Create a ProductBL Class without Method Chaining in C#

Let's start by defining a ProductBL class with standard methods. These methods will be invoked in sequence in the Main method:

public class ProductBL
{
    public Guid ProductId { get; set; }
    public string ProductName { get; set; }
    public decimal Price { get; set; }

    // Other relevant properties for the Product class

    public ProductBL InitializeProduct(Guid ProductId, string ProductName)
    {
        this.ProductId = ProductId == Guid.Empty ? Guid.NewGuid() : ProductId;
        this.ProductName = ProductName;
        this.Price = 100;  // Default price
        return this;
    }

    public void ValidateProduct()
    {
        // Validation logic for the product
    }

    public void UpdatePrice(decimal newPrice)
    {
        // Logic to update the price of the product
        this.Price = newPrice;
    }

    public void SaveProduct()
    {
        // Logic to save the product in the database
    }
}

In the Main method, you would call each method individually.

static void Main(string[] args)
{
    Guid ProductId = Guid.NewGuid();
    string ProductName = "Laptop";
    ProductBL productBL = new ProductBL();
    
    productBL.InitializeProduct(ProductId, ProductName);
    productBL.ValidateProduct();
    productBL.UpdatePrice(120);
    productBL.SaveProduct();
    
    Console.ReadKey();
}

Create a ProductBL Class with Method Chaining in C#

Now, let's modify the ProductBL class to support method chaining by returning the ProductBL instance from each method:

public class ProductBL
{
    public Guid ProductId { get; set; }
    public string ProductName { get; set; }
    public decimal Price { get; set; }

    // Other properties for the Product class

    public ProductBL InitializeProduct(Guid ProductId, string ProductName)
    {
        this.ProductId = ProductId == Guid.Empty ? Guid.NewGuid() : ProductId;
        this.ProductName = ProductName;
        this.Price = 100;  // Default price
        return this;
    }

    public ProductBL ValidateProduct()
    {
        // Validation logic
        return this;
    }

    public ProductBL UpdatePrice(decimal newPrice)
    {
        this.Price = newPrice;
        return this;
    }

    public void SaveProduct()
    {
        // Logic to save the product in the database
    }
}

With method chaining in place, the Main method now becomes more concise:

static void Main(string[] args)
{
    Guid ProductId = Guid.NewGuid();
    string ProductName = "Laptop";
    ProductBL productBL = new ProductBL();
    
    productBL.InitializeProduct(ProductId, ProductName)
             .ValidateProduct()
             .UpdatePrice(120)
             .SaveProduct();
    
    Console.ReadKey();
}

Note that, as before, the method SaveProduct() does not return a ProductBL instance, so the chain ends there.

Advantages of Method Chaining and Fluent Interfaces

Method chaining and fluent interfaces allow for a more readable, expressive, and compact way of writing code. By chaining methods, you can reduce the need for repetitive code and make the flow of operations easier to follow. This can be particularly useful for configurations or operations on complex objects, and it helps improve code maintainability and simplicity.