using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace Dapper { public class SqlBuilder { private readonly Dictionary _data = new Dictionary(); private int _seq; class Clause { public string Sql { get; set; } public object Parameters { get; set; } public bool IsInclusive { get; set; } } class Clauses : List { private readonly string _joiner; private readonly string _prefix; private readonly string _postfix; public Clauses(string joiner, string prefix = "", string postfix = "") { _joiner = joiner; _prefix = prefix; _postfix = postfix; } public string ResolveClauses(DynamicParameters p) { foreach (var item in this) { p.AddDynamicParams(item.Parameters); } return this.Any(a => a.IsInclusive) ? _prefix + string.Join(_joiner, this.Where(a => !a.IsInclusive) .Select(c => c.Sql) .Union(new[] { " ( " + string.Join(" OR ", this.Where(a => a.IsInclusive).Select(c => c.Sql).ToArray()) + " ) " }).ToArray()) + _postfix : _prefix + string.Join(_joiner, this.Select(c => c.Sql).ToArray()) + _postfix; } } public class Template { private readonly string _sql; private readonly SqlBuilder _builder; private readonly object _initParams; private int _dataSeq = -1; // Unresolved public Template(SqlBuilder builder, string sql, dynamic parameters) { _initParams = parameters; _sql = sql; _builder = builder; } private static readonly Regex _regex = new Regex(@"\/\*\*.+\*\*\/", RegexOptions.Compiled | RegexOptions.Multiline); void ResolveSql() { if (_dataSeq != _builder._seq) { DynamicParameters p = new DynamicParameters(_initParams); rawSql = _sql; foreach (var pair in _builder._data) { rawSql = rawSql.Replace("/**" + pair.Key + "**/", pair.Value.ResolveClauses(p)); } parameters = p; // replace all that is left with empty rawSql = _regex.Replace(rawSql, ""); _dataSeq = _builder._seq; } } string rawSql; object parameters; public string RawSql { get { ResolveSql(); return rawSql; } } public object Parameters { get { ResolveSql(); return parameters; } } } public Template AddTemplate(string sql, dynamic parameters = null) { return new Template(this, sql, parameters); } protected void AddClause(string name, string sql, object parameters, string joiner, string prefix = "", string postfix = "", bool isInclusive = false) { Clauses clauses; if (!_data.TryGetValue(name, out clauses)) { clauses = new Clauses(joiner, prefix, postfix); _data[name] = clauses; } clauses.Add(new Clause { Sql = sql, Parameters = parameters, IsInclusive = isInclusive }); _seq++; } public SqlBuilder Intersect(string sql, dynamic parameters = null) { AddClause("intersect", sql, parameters, "\nINTERSECT\n ", "\n ", "\n", false); return this; } public SqlBuilder InnerJoin(string sql, dynamic parameters = null) { AddClause("innerjoin", sql, parameters, "\nINNER JOIN ", "\nINNER JOIN ", "\n", false); return this; } public SqlBuilder LeftJoin(string sql, dynamic parameters = null) { AddClause("leftjoin", sql, parameters, "\nLEFT JOIN ", "\nLEFT JOIN ", "\n", false); return this; } public SqlBuilder RightJoin(string sql, dynamic parameters = null) { AddClause("rightjoin", sql, parameters, "\nRIGHT JOIN ", "\nRIGHT JOIN ", "\n", false); return this; } public SqlBuilder Where(string sql, dynamic parameters = null) { AddClause("where", sql, parameters, " AND ", "WHERE ", "\n", false); return this; } public SqlBuilder OrWhere(string sql, dynamic parameters = null) { AddClause("where", sql, parameters, " AND ", "WHERE ", "\n", true); return this; } public SqlBuilder OrderBy(string sql, dynamic parameters = null) { AddClause("orderby", sql, parameters, " , ", "ORDER BY ", "\n", false); return this; } public SqlBuilder Select(string sql, dynamic parameters = null) { AddClause("select", sql, parameters, " , ", "", "\n", false); return this; } public SqlBuilder AddParameters(dynamic parameters) { AddClause("--parameters", "", parameters, "", "", "", false); return this; } public SqlBuilder Join(string sql, dynamic parameters = null) { AddClause("join", sql, parameters, "\nJOIN ", "\nJOIN ", "\n", false); return this; } public SqlBuilder GroupBy(string sql, dynamic parameters = null) { AddClause("groupby", sql, parameters, " , ", "\nGROUP BY ", "\n", false); return this; } public SqlBuilder Having(string sql, dynamic parameters = null) { AddClause("having", sql, parameters, "\nAND ", "HAVING ", "\n", false); return this; } } }