Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm building a .NET Linq-esc query builder for my application and I'm wondering about my implementation and code structure when it comes a few classes.

Basically I'm creating a query builder for SQL, and I need for my classes to be able to using the following behavior:

query.Select(...).From(...).Join(...).Where(...);

I have created 6 Interfaces that contain one method each that donates what can be done to that class E.G the Select class would implement IFromAble which contains the method From(..). This allows the behavior above.

This is the same for all of the other classes above and a few more. Some classes, like Join will implement a few of the interfaces E.g IJoinAble, IWhereAble, IOrderAble, IGroupAble, IHavingAble.

This, as you can imagine will lead to a lot of duplicated code throughout each of my classes.

Each of the classes also implement a common base abstract class, which means I cannot use abstracts to solve the issue.

Is there another way I can implement my intended behavior without all of the duplicated code? Or is this just one of those cases I'm once again tripped up by C#'s single inheritance?

The Join Class

public class ModuleJoin : QueryModule, IJoinAble, IWhereAble, IOrderAble, IGroupAble
{
    public JoinType JoinType { get; set; }
    public QueryField JoinFrom 
    {
        get { return (QueryField)this.Fields[0]; }
        set { this.Fields[0] = value; } 
    }
    public QueryField JoinTo
    {
        get { return (QueryField)this.Fields[1]; }
        set { this.Fields[1] = value; }
    }

    public ModuleJoin(Query query, JoinType joinType, QueryField joinFrom, QueryField joinTo)
    {
        this.PerentQuery = query;
        this.Fields.Clear();
        this.Fields.Add(joinFrom);
        this.Fields.Add(joinTo);
        JoinType = joinType;
    }

    public ModuleJoin Join(JoinType joinType, QueryField joinFrom, QueryField joinTo)
    {
        ModuleJoin join = new ModuleJoin(this.PerentQuery, joinType, joinFrom, joinTo);
        this.PerentQuery.ModuleJoins.Add(join);
        return join;
    }

    public ModuleWhere Where(IFieldPart itemOne, Comparator comparator, IFieldPart itemTwo)
    {
        ModuleWhere where = new ModuleWhere(this.PerentQuery, itemOne, comparator, itemTwo);
        this.PerentQuery.ModuleWheres.Add(where);
        return where;
    }

    public ModuleGroupBy GroupBy(params IFieldPart[] fields)
    {
        ModuleGroupBy groupBy = new ModuleGroupBy(this.PerentQuery, fields);
        this.PerentQuery.ModuleGroupBy = groupBy;
        return groupBy;
    }
}

QueryModule (The interface on this just has those properties)

public abstract class QueryModule : IQueryModule
{
    public Query PerentQuery { get; set; }
    public FieldCount FieldCount { get; set; }
    public List<IFieldPart> Fields { get; set; }
    public IFieldPart Field
    {
        get { return Fields[0]; }
        set 
        {
            if (Fields.Count == 1)
                Fields[0] = value;
            else
                Fields.Add(value);
        }
    }

    public QueryModule()
    {
        this.Fields = new List<IFieldPart>();
    }
}

IJoinAble (An example of one of the interfaces)

interface IJoinAble
{
    ModuleJoin Join(JoinType joinType, QueryField joinFrom, QueryField joinTo);
}
share|improve this question
2  
As there is no code in the problem, unfortunately this question is off topic. It is rather interesting though :). I would be tempted to just go a fairly standard builder route and have all of the methods be on the same QueryBuilder class. You sacrifice some type safety (query.Select().Select() would be legal), and you have to decide what to do in conflicting situations (Select().Select()), but it would save you a world of headaches, especially since you would end up having each component implement every interface except for itself (what if I want to do query.From(...).Select(...)?). –  Corbin Jul 25 '14 at 14:19
    
Wups, I meant to post this on StackOverflow, is there any way i can migrate it? Thanks for the suggestions, I see where your coming from. Regarding your last point, i have already implemented a way of managing sub-queries :) –  Lex Webb Jul 25 '14 at 14:26
    
Have a single class implement all of your interfaces. Where you need a composite interface, use another interface which just inherits from all the interfaces it composes. You'll need at the very most 1 composite interface per method, hopefully fewer –  Ben Aaronson Jul 25 '14 at 14:38
    
@LexWebb Normally you can flag your post for migration! But I'll verify on meta –  Marc-Andre Jul 25 '14 at 14:42
    
@LexWebb flagging is the way to go : meta.codereview.stackexchange.com/questions/902/… –  Marc-Andre Jul 25 '14 at 14:48

1 Answer 1

up vote 1 down vote accepted

Interesting question. The most simple and direct way to approach this problem (not necessarily the wisest) - implement Select(), From(), Join() etc. as extension methods on the interfaces ISelectable, IFromable, IJoinable, etc. The ability to write extension methods to interfaces is a powerful and fascinating language feature with many implications...

share|improve this answer
    
Perfect. I was able to create a single extension class to manage all of the methods for each interface. Thanks. –  Lex Webb Jul 28 '14 at 10:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.