Search code examples
c#.netfluent

C# Fluent API - How to insure dependant data is chained correctly


I am currently writing a Fluent API (pretty new to this pattern). I am unsure what is the best practice around making sure that dependant data is set via previous method chaining prior to the final execution.

Given the below API.

public class Processor : IFluentProcessor
{
   private string _connectionName = String.Empty;
   private string _whereClause = String.Empty;

   public IFluentProcessor ConnectionName(string connectionName)
   {
       _connectionName = connectionName;
       return this;
   }

   public IFluentProcessor FilterClause(string whereClause)
   {
       _whereClause = whereClause;
       return this;
   }

   public bool Execute(out string errorMessage)
   {
       errorMessage = String.Empty;

       try
       {
           //Ideally i would like to make sure that these variables have been set prior to this execution
           var client = new dbContext(_connectionName);
           var items = client.Where(_whereClause).Select(m => m).ToList();

           foreach (var item in items)
           {
               //process the items here.
           }

           return true;
       }
       catch (Exception ex)
       {
           errorMessage = ex.Message;
           return false;
       }
    }
 }

 public interface IFluentProcessor
 {
     IFluentWorker ConnectionName(string connectionName);
     IFluentProcessor FilterClause(string whereClause);
     bool Execute(out string errorMessage);
 }

Is there a way of insuring that the 'configuration' methods have been previously chained prior to calling the execute method. Rather than just validating the items within the Execute method.


Solution

  • For strict ordering, you could split the interfaces (foregive me the lack of inspiration when naming, you get the idea):

    public interface IFluentProcessor1
    {
        IFluentProcessor2 ConnectionName(string connectionName);
    }
    public interface IFluentProcessor2
    {
        IFluentProcessor3 FilterClause(string whereClause);
    }
    public interface IFluentProcessor3
    {
        bool Execute(out string errorMessage);
    }
    

    Combine that with a private constructor + factory method to instantiate the fluent interface:

    public class Processor : IFluentProcessor1, IFluentProcessor2, IFluentProcessor3
    {
        private string _connectionName = String.Empty;
        private string _whereClause = String.Empty;
    
        private Processor() {}
        public static IFluentProcessor1 Create() { return new Processor(); }
        // ...
    }
    

    This does fix the order in which the methods are called, it does not allow to switch the calls to FilterClause and ConnectionName:

    string errorMessage;
    var result = Processor.Create()
       .ConnectionName("my connection")
       .FilterClause("my filter")
       .Execute(out errorMessage);