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);
}