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

5

8

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.

Kremer answered 26/4, 2016 at 10:17 Comment(3)
Have a look on codebase for FluentAssertions: github.com/dennisdoomen/FluentAssertions they might already have what you need.Gleeman
Your accepted answer can still allow for new ConditionCreator() .Add().Or().And().And().And(). Is that what you wanted or did I misunderstand your question.Pyrargyrite
See my updated answer for more details on how to solve this properly.Breakneck
P
0

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();
}
Pneumatophore answered 26/4, 2016 at 10:21 Comment(0)
B
4

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

Breakneck answered 26/4, 2016 at 10:20 Comment(0)
N
2

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.

Numbersnumbfish answered 26/4, 2016 at 13:37 Comment(0)
P
0

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();
}
Pneumatophore answered 26/4, 2016 at 10:21 Comment(0)
T
0
            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();
}
}
Totalizator answered 26/4, 2016 at 15:17 Comment(0)
J
0

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();
Joanejoanie answered 17/7, 2016 at 19:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.