Skip to content

Instantly share code, notes, and snippets.

@schotime
Created September 8, 2011 00:24
Show Gist options
  • Save schotime/1202262 to your computer and use it in GitHub Desktop.
Save schotime/1202262 to your computer and use it in GitHub Desktop.
Dynamic Sql based on templates
public class SqlBuilder
{
Dictionary<string, Clauses> data = new Dictionary<string, Clauses>();
int seq;
class Clause
{
public string Sql { get; set; }
public List<object> Parameters { get; set; }
}
class Clauses : List<Clause>
{
string joiner;
string prefix;
string postfix;
public Clauses(string joiner, string prefix, string postfix)
{
this.joiner = joiner;
this.prefix = prefix;
this.postfix = postfix;
}
public string ResolveClauses(List<object> finalParams)
{
foreach (var item in this)
{
item.Sql = Database.ProcessParams(item.Sql, item.Parameters.ToArray(), finalParams);
}
return prefix + string.Join(joiner, this.Select(c => c.Sql)) + postfix;
}
}
public class Template
{
readonly string sql;
readonly SqlBuilder builder;
private List<object> finalParams = new List<object>();
int dataSeq;
public Template(SqlBuilder builder, string sql, params object[] parameters)
{
this.sql = Database.ProcessParams(sql, parameters, finalParams);
this.builder = builder;
}
static Regex regex = new Regex(@"\/\*\*.+\*\*\/", RegexOptions.Compiled | RegexOptions.Multiline);
void ResolveSql()
{
rawSql = sql;
if (dataSeq != builder.seq)
{
foreach (var pair in builder.data)
{
rawSql = rawSql.Replace("/**" + pair.Key + "**/", pair.Value.ResolveClauses(finalParams));
}
ReplaceDefaults();
dataSeq = builder.seq;
}
if (builder.seq == 0)
{
ReplaceDefaults();
}
}
private void ReplaceDefaults()
{
foreach (var pair in builder.defaultsIfEmpty)
{
rawSql = rawSql.Replace("/**" + pair.Key + "**/", " " + pair.Value + " ");
}
// replace all that is left with empty
rawSql = regex.Replace(rawSql, "");
}
string rawSql;
public string RawSql { get { ResolveSql(); return rawSql; } }
public object Parameters { get { ResolveSql(); return finalParams; } }
}
public SqlBuilder()
{
}
public Template AddTemplate(string sql, params object[] parameters)
{
return new Template(this, sql, parameters);
}
void AddClause(string name, string sql, object[] parameters, string joiner, string prefix, string postfix)
{
Clauses clauses;
if (!data.TryGetValue(name, out clauses))
{
clauses = new Clauses(joiner, prefix, postfix);
data[name] = clauses;
}
clauses.Add(new Clause { Sql = sql, Parameters = new List<object>(parameters) });
seq++;
}
Dictionary<string, string> defaultsIfEmpty = new Dictionary<string, string>
{
{ "where", "1=1" },
};
public SqlBuilder SelectCols(params string[] columns)
{
AddClause("selectcols", string.Join(", ", columns), new object[] { }, ", ", ", ", "");
return this;
}
public SqlBuilder InnerJoin(string sql, params object[] args)
{
AddClause("innerjoin", sql, args, "\nINNER JOIN ", "\nINNER JOIN ", "\n");
return this;
}
public SqlBuilder LeftJoin(string sql, params object[] args)
{
AddClause("leftjoin", sql, args, "\nLEFT JOIN ", "\nLEFT JOIN ", "\n");
return this;
}
public SqlBuilder Where(string sql, params object[] args)
{
AddClause("where", sql, args, " AND ", " ( ", " )\n");
return this;
}
public SqlBuilder OrderBy(string sql, params object[] args)
{
AddClause("orderby", sql, args, ", ", "ORDER BY ", "\n");
return this;
}
public SqlBuilder OrderByCols(params string[] columns)
{
AddClause("orderbycols", string.Join(", ", columns), new object[] { }, ", ", ", ", "");
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment