Last active
June 14, 2017 18:09
-
-
Save thongdoan/d5e84ae0a640fe2a3d0ef7ce8083a9a9 to your computer and use it in GitHub Desktop.
Sqlite.Net Delete, Update, Insert helper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Thanks to https://github.com/praeclarum/sqlite-net | |
public static class SQLiteExtension | |
{ | |
public static StatementBuilder<T> CreateStatement<T>(this SQLiteConnection conn) | |
{ | |
return new StatementBuilder<T>(conn); | |
} | |
} | |
public class StatementBuilder<T> | |
{ | |
private Expression _where; | |
private Dictionary<string, object> _values; | |
private SQLiteConnection _connection; | |
private TableMapping _table; | |
public StatementBuilder(SQLiteConnection connection) | |
{ | |
_connection = connection; | |
_table = _connection.GetMapping<T>(); | |
_values = new Dictionary<string, object>(); | |
} | |
private StatementBuilder() | |
{ | |
} | |
public StatementBuilder<T> Set(string name, object value) | |
{ | |
if (value is Expression valueExpr) | |
{ | |
if (valueExpr.NodeType == ExpressionType.Lambda) | |
{ | |
var lambda = (LambdaExpression)valueExpr; | |
value = lambda.Body; | |
} | |
else | |
{ | |
throw new NotSupportedException("value must be a lambda expression"); | |
} | |
} | |
_values[name] = value; | |
return this; | |
} | |
public StatementBuilder<T> Set(Expression<Func<T, object>> expr, object value) | |
{ | |
return Set(GetColumnName(expr), value); | |
} | |
public StatementBuilder<T> Set(Expression<Func<T, object>> expr, Expression<Func<T, object>> value) | |
{ | |
return Set(GetColumnName(expr), value); | |
} | |
public StatementBuilder<T> SetRaw(Expression<Func<T, object>> expr, string rawSql, params object[] args) | |
{ | |
return Set(GetColumnName(expr), new RawSql(rawSql, args)); | |
} | |
public StatementBuilder<T> UnSet(Expression<Func<T, object>> expr) | |
{ | |
return UnSet(GetColumnName(expr)); | |
} | |
public StatementBuilder<T> UnSet(string column) | |
{ | |
_values.Remove(column); | |
return this; | |
} | |
public StatementBuilder<T> Where(Expression<Func<T, bool>> predExpr) | |
{ | |
if (predExpr.NodeType == ExpressionType.Lambda) | |
{ | |
var lambda = (LambdaExpression)predExpr; | |
var pred = lambda.Body; | |
AddWhere(pred); | |
return this; | |
} | |
else | |
{ | |
throw new NotSupportedException("Must be a predicate"); | |
} | |
} | |
public StatementBuilder<T> ChangeWhere(Expression<Func<T, bool>> predExpr) | |
{ | |
_where = predExpr; | |
return this; | |
} | |
public StatementBuilder<T> PrepareForReuse() | |
{ | |
_where = null; | |
_values.Clear(); | |
return this; | |
} | |
public int ExecuteUpdate() | |
{ | |
if (_values.Count == 0) | |
return 0; | |
var commandText = new StringBuilder("update \"" + _table.TableName + "\" set "); | |
var args = new List<object>(); | |
foreach (var kv in _values) | |
{ | |
switch (kv.Value) | |
{ | |
case Expression expr: | |
var v = CompileExpr(kv.Value as Expression, args); | |
commandText.Append("\"" + kv.Key + "\" = " + v.CommandText + ", "); | |
break; | |
case RawSql sql: | |
commandText.Append("\"" + kv.Key + "\" = " + sql.Sql + ", "); | |
if (args.Count > 0) | |
args.AddRange(args); | |
break; | |
default: | |
commandText.Append("\"" + kv.Key + "\" = ?, "); | |
args.Add(kv.Value); | |
break; | |
} | |
} | |
commandText.Remove(commandText.Length - 2, 2); | |
if (_where != null) | |
{ | |
commandText.Append(" where "); | |
var w = CompileExpr(_where, args); | |
commandText.Append(w.CommandText); | |
} | |
return _connection.Execute(commandText.ToString(), (object[])args.ToArray()); | |
} | |
public int ExecuteDelete() | |
{ | |
var args = new List<object>(); | |
var commandText = new StringBuilder("delete from \"" + _table.TableName + "\" "); | |
if (_where != null) | |
{ | |
commandText.Append(" where "); | |
var w = CompileExpr(_where, args); | |
commandText.Append(w.CommandText); | |
} | |
return _connection.Execute(commandText.ToString(), (object[])args.ToArray()); | |
} | |
public int ExecuteInsert() | |
{ | |
return ExecuteInsert("insert into"); | |
} | |
public int ExecuteInsertOrReplace() | |
{ | |
return ExecuteInsert("insert or replace into"); | |
} | |
private int ExecuteInsert(string prefix) | |
{ | |
var commandText = new StringBuilder(); | |
commandText.Append(prefix + " \"" + _table.TableName + "\" "); | |
var args = new List<object>(); | |
var columns = new StringBuilder(" ("); | |
var values = new StringBuilder(" values ("); | |
foreach (var kv in _values) | |
{ | |
columns.Append("\"" + kv.Key + "\", "); | |
values.Append("?, "); | |
args.Add(kv.Value); | |
} | |
columns.Remove(columns.Length - 2, 2); | |
values.Remove(values.Length - 2, 2); | |
columns.Append(") "); | |
values.Append(")"); | |
commandText.Append(columns); | |
commandText.Append(values); | |
return _connection.Execute(commandText.ToString(), (object[])args.ToArray()); | |
} | |
public StatementBuilder<T> Clone() | |
{ | |
var s = new StatementBuilder<T>() | |
{ | |
_connection = _connection, | |
_table = _table, | |
_values = new Dictionary<string, object>(_values ?? new Dictionary<string, object>()), | |
_where = _where | |
}; | |
return s; | |
} | |
private string GetColumnName(Expression<Func<T, object>> predExpr) | |
{ | |
var propertyName = GetPropertyName(predExpr); | |
var column = _table.FindColumnWithPropertyName(propertyName); | |
if (column == null) | |
throw new InvalidOperationException("Not found column"); | |
return column.Name; | |
} | |
private string GetPropertyName(Expression<Func<T, object>> expr) | |
{ | |
MemberExpression body = expr.Body as MemberExpression; | |
if (body == null) | |
{ | |
UnaryExpression ubody = (UnaryExpression)expr.Body; | |
body = ubody.Operand as MemberExpression; | |
} | |
if (body == null) | |
throw new NotSupportedException("expr must be MemberExpression"); | |
return body.Member.Name; | |
} | |
private StatementBuilder<T> AddWhere(Expression pred) | |
{ | |
if (_where == null) | |
{ | |
_where = pred; | |
} | |
else | |
{ | |
_where = Expression.AndAlso(_where, pred); | |
} | |
return this; | |
} | |
//This class is copied from Sqlite.Net TableQuery class | |
class CompileResult | |
{ | |
public string CommandText { get; set; } | |
public object Value { get; set; } | |
} | |
//This method is copied and modify from Sqlite.Net TableQuery class | |
private CompileResult CompileExpr(Expression expr, List<object> queryArgs) | |
{ | |
if (expr == null) | |
{ | |
throw new NotSupportedException("Expression is NULL"); | |
} | |
else if (expr is BinaryExpression) | |
{ | |
var bin = (BinaryExpression)expr; | |
var leftr = CompileExpr(bin.Left, queryArgs); | |
var rightr = CompileExpr(bin.Right, queryArgs); | |
//If either side is a parameter and is null, then handle the other side specially (for "is null"/"is not null") | |
string text; | |
if (leftr.CommandText == "?" && leftr.Value == null) | |
text = CompileNullBinaryExpression(bin, rightr); | |
else if (rightr.CommandText == "?" && rightr.Value == null) | |
text = CompileNullBinaryExpression(bin, leftr); | |
else | |
text = "(" + leftr.CommandText + " " + GetSqlName(bin) + " " + rightr.CommandText + ")"; | |
return new CompileResult { CommandText = text }; | |
} | |
else if (expr.NodeType == ExpressionType.Call) | |
{ | |
var call = (MethodCallExpression)expr; | |
var args = new CompileResult[call.Arguments.Count]; | |
var obj = call.Object != null ? CompileExpr(call.Object, queryArgs) : null; | |
for (var i = 0; i < args.Length; i++) | |
{ | |
args[i] = CompileExpr(call.Arguments[i], queryArgs); | |
} | |
var sqlCall = ""; | |
if (call.Method.Name == "Like" && args.Length == 2) | |
{ | |
sqlCall = "(" + args[0].CommandText + " like " + args[1].CommandText + ")"; | |
} | |
else if (call.Method.Name == "Contains" && args.Length == 2) | |
{ | |
sqlCall = "(" + args[1].CommandText + " in " + args[0].CommandText + ")"; | |
} | |
else if (call.Method.Name == "Contains" && args.Length == 1) | |
{ | |
if (call.Object != null && call.Object.Type == typeof(string)) | |
{ | |
sqlCall = "(" + obj.CommandText + " like ('%' || " + args[0].CommandText + " || '%'))"; | |
} | |
else | |
{ | |
sqlCall = "(" + args[0].CommandText + " in " + obj.CommandText + ")"; | |
} | |
} | |
else if (call.Method.Name == "StartsWith" && args.Length == 1) | |
{ | |
sqlCall = "(" + obj.CommandText + " like (" + args[0].CommandText + " || '%'))"; | |
} | |
else if (call.Method.Name == "EndsWith" && args.Length == 1) | |
{ | |
sqlCall = "(" + obj.CommandText + " like ('%' || " + args[0].CommandText + "))"; | |
} | |
else if (call.Method.Name == "Equals" && args.Length == 1) | |
{ | |
sqlCall = "(" + obj.CommandText + " = (" + args[0].CommandText + "))"; | |
} | |
else if (call.Method.Name == "ToLower") | |
{ | |
sqlCall = "(lower(" + obj.CommandText + "))"; | |
} | |
else if (call.Method.Name == "ToUpper") | |
{ | |
sqlCall = "(upper(" + obj.CommandText + "))"; | |
} | |
else | |
{ | |
sqlCall = call.Method.Name.ToLower() + "(" + string.Join(",", args.Select(a => a.CommandText).ToArray()) + ")"; | |
} | |
return new CompileResult { CommandText = sqlCall }; | |
} | |
else if (expr.NodeType == ExpressionType.Constant) | |
{ | |
var c = (ConstantExpression)expr; | |
queryArgs.Add(c.Value); | |
return new CompileResult | |
{ | |
CommandText = "?", | |
Value = c.Value | |
}; | |
} | |
else if (expr.NodeType == ExpressionType.Convert) | |
{ | |
var u = (UnaryExpression)expr; | |
var ty = u.Type; | |
var valr = CompileExpr(u.Operand, queryArgs); | |
return new CompileResult | |
{ | |
CommandText = valr.CommandText, | |
Value = valr.Value != null ? ConvertTo(valr.Value, ty) : null | |
}; | |
} | |
else if (expr.NodeType == ExpressionType.MemberAccess) | |
{ | |
var mem = (MemberExpression)expr; | |
if (mem.Expression != null && mem.Expression.NodeType == ExpressionType.Parameter) | |
{ | |
// | |
// This is a column of our table, output just the column name | |
// Need to translate it if that column name is mapped | |
// | |
var columnName = _table.FindColumnWithPropertyName(mem.Member.Name).Name; | |
return new CompileResult { CommandText = "\"" + columnName + "\"" }; | |
} | |
else | |
{ | |
object obj = null; | |
if (mem.Expression != null) | |
{ | |
var r = CompileExpr(mem.Expression, queryArgs); | |
if (r.Value == null) | |
{ | |
throw new NotSupportedException("Member access failed to compile expression"); | |
} | |
if (r.CommandText == "?") | |
{ | |
queryArgs.RemoveAt(queryArgs.Count - 1); | |
} | |
obj = r.Value; | |
} | |
// | |
// Get the member value | |
// | |
object val = null; | |
#if !NETFX_CORE | |
if (mem.Member.MemberType == MemberTypes.Property) | |
{ | |
#else | |
if (mem.Member is PropertyInfo) { | |
#endif | |
var m = (PropertyInfo)mem.Member; | |
val = m.GetValue(obj, null); | |
#if !NETFX_CORE | |
} | |
else if (mem.Member.MemberType == MemberTypes.Field) | |
{ | |
#else | |
} else if (mem.Member is FieldInfo) { | |
#endif | |
#if SILVERLIGHT | |
val = Expression.Lambda (expr).Compile ().DynamicInvoke (); | |
#else | |
var m = (FieldInfo)mem.Member; | |
val = m.GetValue(obj); | |
#endif | |
} | |
else | |
{ | |
#if !NETFX_CORE | |
throw new NotSupportedException("MemberExpr: " + mem.Member.MemberType); | |
#else | |
throw new NotSupportedException ("MemberExpr: " + mem.Member.DeclaringType); | |
#endif | |
} | |
// | |
// Work special magic for enumerables | |
// | |
if (val != null && val is System.Collections.IEnumerable && !(val is string) && !(val is System.Collections.Generic.IEnumerable<byte>)) | |
{ | |
var sb = new System.Text.StringBuilder(); | |
sb.Append("("); | |
var head = ""; | |
foreach (var a in (System.Collections.IEnumerable)val) | |
{ | |
queryArgs.Add(a); | |
sb.Append(head); | |
sb.Append("?"); | |
head = ","; | |
} | |
sb.Append(")"); | |
return new CompileResult | |
{ | |
CommandText = sb.ToString(), | |
Value = val | |
}; | |
} | |
else | |
{ | |
queryArgs.Add(val); | |
return new CompileResult | |
{ | |
CommandText = "?", | |
Value = val | |
}; | |
} | |
} | |
} | |
throw new NotSupportedException("Cannot compile: " + expr.NodeType.ToString()); | |
} | |
/// <summary> | |
/// Compiles a BinaryExpression where one of the parameters is null. | |
/// </summary> | |
/// <param name="parameter">The non-null parameter</param> | |
private string CompileNullBinaryExpression(BinaryExpression expression, CompileResult parameter) | |
{ | |
if (expression.NodeType == ExpressionType.Equal) | |
return "(" + parameter.CommandText + " is ?)"; | |
else if (expression.NodeType == ExpressionType.NotEqual) | |
return "(" + parameter.CommandText + " is not ?)"; | |
else | |
throw new NotSupportedException("Cannot compile Null-BinaryExpression with type " + expression.NodeType.ToString()); | |
} | |
static object ConvertTo(object obj, Type t) | |
{ | |
Type nut = Nullable.GetUnderlyingType(t); | |
if (nut != null) | |
{ | |
if (obj == null) return null; | |
return Convert.ChangeType(obj, nut); | |
} | |
else | |
{ | |
return Convert.ChangeType(obj, t); | |
} | |
} | |
string GetSqlName(Expression expr) | |
{ | |
var n = expr.NodeType; | |
if (n == ExpressionType.GreaterThan) | |
return ">"; | |
else if (n == ExpressionType.GreaterThanOrEqual) | |
{ | |
return ">="; | |
} | |
else if (n == ExpressionType.LessThan) | |
{ | |
return "<"; | |
} | |
else if (n == ExpressionType.LessThanOrEqual) | |
{ | |
return "<="; | |
} | |
else if (n == ExpressionType.And) | |
{ | |
return "&"; | |
} | |
else if (n == ExpressionType.AndAlso) | |
{ | |
return "and"; | |
} | |
else if (n == ExpressionType.Or) | |
{ | |
return "|"; | |
} | |
else if (n == ExpressionType.OrElse) | |
{ | |
return "or"; | |
} | |
else if (n == ExpressionType.Equal) | |
{ | |
return "="; | |
} | |
else if (n == ExpressionType.NotEqual) | |
{ | |
return "!="; | |
} | |
else if (n == ExpressionType.Add) | |
{ | |
if (expr.Type.IsAssignableFrom(typeof(string))) | |
return "||"; | |
return "+"; | |
} | |
else if (n == ExpressionType.Subtract) | |
{ | |
return "-"; | |
} | |
else if (n == ExpressionType.Multiply) | |
{ | |
return "*"; | |
} | |
else if (n == ExpressionType.Divide) | |
{ | |
return "/"; | |
} | |
else if (n == ExpressionType.Modulo) | |
{ | |
return "%"; | |
} | |
else if (n == ExpressionType.LeftShift) | |
{ | |
return "<<"; | |
} | |
else if (n == ExpressionType.RightShift) | |
{ | |
return ">>"; | |
} | |
else | |
{ | |
throw new NotSupportedException("Cannot get SQL for: " + n); | |
} | |
} | |
private class RawSql | |
{ | |
public string Sql { get; private set; } | |
public object[] Args { get; set; } | |
public RawSql(string value, object[] args) | |
{ | |
this.Sql = value; | |
this.Args = args; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example: