HamedFathi
HamedFathi

Reputation: 3969

How create Fluent Interface in C# with some limitation for some methods?

See below codes :

new ConditionCreator()
       .Add()
             .Or()
       .Add()
             .And()
       .Add()

I want to create a Fluent Interface for that But I need, after Add() method developer see Only Or() or And() and after one of these, see Only Add() method.

so no one can write a code like :

new ConditionCreator()
           .Add()
           .Add()
           .Add()
           .Or()
           .And()
           .Add()
           .And()
           .And()

I want to have a limitation for some methods can accept special methods and etc. I can write all methods in one class and return this for each one but that is not suitable !!!

Please guide me How write Advanced Fluent Interface class.

Upvotes: 8

Views: 1309

Answers (5)

NQA
NQA

Reputation: 345

This seems to work.

  public class ConditionCreator
  {
     private Decision decision;

     public ConditionCreator() { decision = new Decision(this); }
     public Decision Add() { return decision; }

     public class Decision
     {
        private ConditionCreator creator;

        public Decision(ConditionCreator creator) { this.creator = creator; }
        public ConditionCreator And() { return creator; }
        public ConditionCreator Or() { return creator; }
        public Condition Create() { return new Condition(); }
     }
  }

And you're now restricted to patterns like this when you make the calls:

     var condition = new ConditionCreator()
         .Add()
         .Or()
         .Add()
         .And()
         .Add()
         .Create();

Upvotes: 0

Dinesh Tripathi
Dinesh Tripathi

Reputation: 224

            public class DoEqual
                {

                }
                public interface ICanAddWhereValue
                {
                    ICanAddWhereOrRun IsEqualTo(object value);
                    ICanAddWhereOrRun IsNotEqualTo(object value);
                    IBothEqual BothEqual ( object value );
                }

                public interface IBothEqual
                {
                    DoEqual Excute();
                }


                public interface ICanAddWhereOrRun
                {
                    ICanAddWhereValue Where(string columnName);
                    bool RunNow();
                    DoEqual Excute();
                }

             public interface ICanAddCondition
                {
                    ICanAddWhereValue Where(string columnName);
                    bool AllRows();
                }

        namespace BuildAFluentInterface
        {
            public class WhereCondition
            {
                public enum ComparisonMethod
                {
                    EqualTo,
                    NotEqualTo
                }

                public string ColumnName { get; private set; }
                public ComparisonMethod Comparator { get; private set; }
                public object Value { get; private set; }

                public WhereCondition(string columnName, ComparisonMethod comparator, object value)
                {
                    ColumnName = columnName;
                    Comparator = comparator;
                    Value = value;
                }
            }
        }

    using System.Collections.Generic;

    namespace BuildAFluentInterface
    {
        public class DeleteQueryWithoutGrammar
        {
            private readonly string _tableName;
            private readonly List<WhereCondition> _whereConditions = new List<WhereCondition>();

            private string _currentWhereConditionColumn;

            // Private constructor, to force object instantiation from the fluent method(s)
            private DeleteQueryWithoutGrammar(string tableName)
            {
                _tableName = tableName;
            }

            #region Initiating Method(s)

            public static DeleteQueryWithoutGrammar DeleteRowsFrom(string tableName)
            {
                return new DeleteQueryWithoutGrammar(tableName);
            }

            #endregion

            #region Chaining Method(s)

            public DeleteQueryWithoutGrammar Where(string columnName)
            {
                _currentWhereConditionColumn = columnName;

                return this;
            }

            public DeleteQueryWithoutGrammar IsEqualTo(object value)
            {
                _whereConditions.Add(new WhereCondition(_currentWhereConditionColumn, WhereCondition.ComparisonMethod.EqualTo, value));

                return this;
            }

            public DeleteQueryWithoutGrammar IsNotEqualTo(object value)
            {
                _whereConditions.Add(new WhereCondition(_currentWhereConditionColumn, WhereCondition.ComparisonMethod.NotEqualTo, value));

                return this;
            }

            #endregion

            #region Executing Method(s)

            public void AllRows()
            {
                ExecuteThisQuery();
            }

            public void RunNow()
            {
                ExecuteThisQuery();
            }

            #endregion

            private void ExecuteThisQuery()
            {
                // Code to build and execute the delete query
            }
        }
    }
<br>
In Main Test with 
public class myclass
{
private static void Main(string[] args)
        {
DoEqual x3 =
                DeleteQueryWithGrammar.DeleteRowsFrom("Account")
                    .Where("Admin")
                    .IsNotEqualTo("Admin")
                    .Where("Admin")
                    .BothEqual("X")
                    .Excute();
}
}

Upvotes: 0

Meirion Hughes
Meirion Hughes

Reputation: 26408

There is no real easy-way I know of to solve this. Perhaps T4 templating may help, but thus far I've always had to build-up the decision-tree, with an explicit interface at each node. For example; lets assume your decision tree is an infinite loop, then (implemented accordingly):

interface IStart<T>
{
   IAndOr Add();
   T End();
}
interface IAndOr<T>
{
   IStart<T> And();
   IStart<T> Or();
}

It gets difficult if you want a finite loop; say zero to two Adds:

interface IStart<T> : IFinish<T>
{
   IAndOrFirst<T> Add();
}

interface IAndOrFirst<T>
{
   ISecond<T> And();
   ISecond<T> Or();
}

interface ISecond<T> : IFinish<T>
{
   IAndOrSecond<T> Add();
}

interface IAndOrSecond <T>
{
   IFinish<T> And();
   IFinish<T> Or();
}    
interface IFinish<T>
{      
   T End();
}

You can (explicitly) implement these in a single class that acts as the state machine:

class ConditionCreator <T> : IStart<T>, IFinish<T>, IAndOrFirst<T>, IAndOrSecond<T> {...}

where you'd return this for Add() And() Or() and maintain those state changes and order.

I'm hoping some answers this question with a better way that manually writing out each node.

Upvotes: 2

Macke
Macke

Reputation: 25680

To restrict things, you need to create and return one (of possibly several) "builder" objects that can do special operations, keeping a ref to the main class.

public class ConditionCreator 
{
    public ConditionCreator() { ... }

    public SubConditionCreator Add() { ...; return new SubConditionCreator(this); }

    internal ConditionCreator OnAdd() { ...; return this; };
    internal ConditionCreator OnOr() { ...; return this; };
}

public class SubConditionCreator
{
    private ConditionCreator _creator;

    internal SubConditionCreator(ConditionCreator c) { _creator = c; }

    public ConditionCreator And() { return _creator.OnAdd(); }
    public ConditionCreator Or() { return _creator.OnOr(); }
}

Use internal access to restrict usage.

To avoid creating garbage, store a SubConditionCreator ref in main class

Upvotes: 4

yaakov
yaakov

Reputation: 5851

Consider returning an interface that contains only And() and Or(). For example:

public class ConditionCreator : IFluentAndOr
{
    public IFluentAndOr And() { ... }
    public IFluentAndOr Or() { ... }
}

public interface IFluentAndOr
{
    IFluentAndOr And();
    IFluentAndOr Or();
}

Upvotes: 0

Related Questions