0

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.

DotNetHitMan
  • 931
  • 8
  • 20
  • This [related answer](http://stackoverflow.com/a/18692402/5233410) also helps in addition to the answer you accepted. – Nkosi Jan 25 '16 at 13:12

1 Answers1

4

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);
jeroenh
  • 26,362
  • 10
  • 73
  • 104
  • I agree and would like to add that this approach is a good fit for source generation as it follows a strict pattern. I have published a library that does exactly that: [M31.FluentAPI](https://github.com/m31coding/M31.FluentAPI). – kmschaal Feb 25 '23 at 14:50