Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit cf1d0f4

Browse files
committed
Implemented an UpdateIncrement API
Implement an UpdateIncrement API similar to UpdateOnly, that generates SQL to add the value to the existing database field instead of doing attribution. db.UpdateIncrement(new Person { Age = 5 }, db.From<Person>().Update(p => p.Age).Where(x => x.FirstName == "Jimi")); will generate this SQL: UPDATE "Person" SET "Age" = "Age" + 5 WHERE ("FirstName" = 'Jimi') This is usefull to increment counter, totals, etc. without having to load the object from the BD first and avoinding concurrency conflicts.
1 parent f70b658 commit cf1d0f4

File tree

6 files changed

+148
-0
lines changed

6 files changed

+148
-0
lines changed

src/ServiceStack.OrmLite/Expressions/WriteExpressionCommandExtensions.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,38 @@ public static int UpdateOnly<T, TKey>(this IDbCommand dbCmd, T obj,
5151
return dbCmd.UpdateOnly(obj, q);
5252
}
5353

54+
public static int UpdateIncrement<T>(this IDbCommand dbCmd, T model, SqlExpression<T> fields)
55+
{
56+
UpdateIncrementSql(dbCmd, model, fields);
57+
return dbCmd.ExecNonQuery();
58+
}
59+
60+
internal static void UpdateIncrementSql<T>(this IDbCommand dbCmd, T model, SqlExpression<T> fields)
61+
{
62+
if (OrmLiteConfig.UpdateFilter != null)
63+
OrmLiteConfig.UpdateFilter(dbCmd, model);
64+
65+
fields.CopyParamsTo(dbCmd);
66+
67+
dbCmd.GetDialectProvider().PrepareUpdateRowIncrementStatement(dbCmd, model, fields.UpdateFields);
68+
69+
if (!fields.WhereExpression.IsNullOrEmpty())
70+
dbCmd.CommandText += " " + fields.WhereExpression;
71+
}
72+
73+
public static int UpdateIncrement<T, TKey>(this IDbCommand dbCmd, T obj,
74+
Expression<Func<T, TKey>> fields = null,
75+
Expression<Func<T, bool>> where = null)
76+
{
77+
if (fields == null)
78+
throw new ArgumentNullException("fields");
79+
80+
var q = dbCmd.GetDialectProvider().SqlExpression<T>();
81+
q.Update(fields);
82+
q.Where(where);
83+
return dbCmd.UpdateIncrement(obj, q);
84+
}
85+
5486
public static int UpdateNonDefaults<T>(this IDbCommand dbCmd, T item, Expression<Func<T, bool>> obj)
5587
{
5688
if (OrmLiteConfig.UpdateFilter != null)

src/ServiceStack.OrmLite/IOrmLiteDialectProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ string GetColumnDefinition(
121121

122122
void PrepareUpdateRowStatement(IDbCommand dbCmd, object objWithProperties, ICollection<string> UpdateFields = null);
123123

124+
void PrepareUpdateRowIncrementStatement(IDbCommand dbCmd, object objWithProperties, ICollection<string> UpdateFields);
125+
124126
string ToDeleteStatement(Type tableType, string sqlFilter, params object[] filterParams);
125127

126128
IDbCommand CreateParameterizedDeleteStatement(IDbConnection connection, object objWithProperties);

src/ServiceStack.OrmLite/OrmLiteDialectProviderBase.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,59 @@ public virtual void PrepareUpdateRowStatement(IDbCommand dbCmd, object objWithPr
949949
throw new Exception("No valid update properties provided (e.g. p => p.FirstName): " + dbCmd.CommandText);
950950
}
951951

952+
public virtual void PrepareUpdateRowIncrementStatement(IDbCommand dbCmd, object objWithProperties, ICollection<string> updateFields)
953+
{
954+
if (updateFields.Count == 0)
955+
throw new Exception("No valid update properties provided (e.g. p => p.FirstName): " + dbCmd.CommandText);
956+
957+
var sqlFilter = new StringBuilder();
958+
var sql = new StringBuilder();
959+
var modelDef = objWithProperties.GetType().GetModelDefinition();
960+
var quotedFieldName = string.Empty;
961+
962+
foreach (var fieldDef in modelDef.FieldDefinitions)
963+
{
964+
if (!updateFields.Contains(fieldDef.FieldName))
965+
continue;
966+
967+
if (fieldDef.AutoIncrement || fieldDef.IsComputed || fieldDef.IsPrimaryKey ||
968+
fieldDef.IsRowVersion || fieldDef.Name == OrmLiteConfig.IdField)
969+
continue;
970+
971+
try
972+
{
973+
if (sql.Length > 0)
974+
sql.Append(", ");
975+
976+
quotedFieldName = GetQuotedColumnName(fieldDef.FieldName);
977+
978+
if (fieldDef.FieldType.IsNumericType())
979+
{
980+
sql
981+
.Append(quotedFieldName)
982+
.Append("=")
983+
.Append(quotedFieldName)
984+
.Append("+")
985+
.Append(this.AddParam(dbCmd, fieldDef.GetValue(objWithProperties), fieldDef.ColumnType).ParameterName);
986+
}
987+
else
988+
{
989+
sql
990+
.Append(quotedFieldName)
991+
.Append("=")
992+
.Append(this.AddParam(dbCmd, fieldDef.GetValue(objWithProperties), fieldDef.ColumnType).ParameterName);
993+
}
994+
}
995+
catch (Exception ex)
996+
{
997+
Log.Error("ERROR in ToUpdateIncrementRowStatement(): " + ex.Message, ex);
998+
}
999+
}
1000+
1001+
dbCmd.CommandText = string.Format("UPDATE {0} SET {1}{2}",
1002+
GetQuotedTableName(modelDef), sql, (sqlFilter.Length > 0 ? " WHERE " + sqlFilter : ""));
1003+
}
1004+
9521005
public virtual string ToDeleteStatement(Type tableType, string sqlFilter, params object[] filterParams)
9531006
{
9541007
var sql = new StringBuilder();

src/ServiceStack.OrmLite/OrmLiteWriteExpressionsApi.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,43 @@ public static int UpdateOnly<T, TKey>(this IDbConnection dbConn, T obj,
5656
return dbConn.Exec(dbCmd => dbCmd.UpdateOnly(obj, onlyFields, where));
5757
}
5858

59+
/// <summary>
60+
/// Use an SqlExpression to select which fields to update and construct the where expression, E.g:
61+
/// Numeric fields generates an increment sql which is usefull to increment counters, etc...
62+
/// avoiding concurrency conflicts
63+
///
64+
/// var q = db.From&gt;Person&lt;());
65+
/// db.UpdateIncrement(new Person { Age = 5 }, db.From<Person>().Update(p => p.Age).Where(x => x.FirstName == "Jimi"));
66+
/// UPDATE "Person" SET "Age" = "Age" + 5 WHERE ("FirstName" = 'Jimi')
67+
///
68+
/// What's not in the update expression doesn't get updated. No where expression updates all rows. E.g:
69+
///
70+
/// db.UpdateIncrement(new Person { Age = 5, FirstName = "JJ", LastName = "Hendo" }, ev.Update(p => p.Age));
71+
/// UPDATE "Person" SET "Age" = "Age" + 5
72+
/// </summary>
73+
public static int UpdateIncrement<T>(this IDbConnection dbConn, T model, SqlExpression<T> fields)
74+
{
75+
return dbConn.Exec(dbCmd => dbCmd.UpdateIncrement(model, fields));
76+
}
77+
78+
/// <summary>
79+
/// Update record, updating only fields specified in updateOnly that matches the where condition (if any), E.g:
80+
/// Numeric fields generates an increment sql which is usefull to increment counters, etc...
81+
/// avoiding concurrency conflicts
82+
///
83+
/// db.UpdateIncrement(new Person { Age = 5 }, p => p.Age, p => p.LastName == "Hendrix");
84+
/// UPDATE "Person" SET "Age" = "Age" + 5 WHERE ("LastName" = 'Hendrix')
85+
///
86+
/// db.UpdateIncrement(new Person { Age = 5 }, p => p.FirstName);
87+
/// UPDATE "Person" SET "Age" = "Age" + 5
88+
/// </summary>
89+
public static int UpdateIncrement<T, TKey>(this IDbConnection dbConn, T obj,
90+
Expression<Func<T, TKey>> fields = null,
91+
Expression<Func<T, bool>> where = null)
92+
{
93+
return dbConn.Exec(dbCmd => dbCmd.UpdateIncrement(obj, fields, where));
94+
}
95+
5996
/// <summary>
6097
/// Updates all non-default values set on item matching the where condition (if any). E.g
6198
///

tests/ServiceStack.OrmLite.Tests/ApiSqlServerTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,18 @@ public void API_SqlServer_Parameterized_Examples()
324324
db.UpdateOnly(new Person { FirstName = "JJ" }, q => q.Update(x => x.FirstName).Where(x => x.FirstName == "Jimi"));
325325
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"FirstName\"=@1 WHERE (\"FirstName\" = @0)"));
326326

327+
db.UpdateIncrement(new Person { Age = 3 }, x => x.Age);
328+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"Age\"=\"Age\"+@0"));
329+
330+
db.UpdateIncrement(new Person { Age = 5 }, x => x.Age, x => x.LastName == "Presley");
331+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"Age\"=\"Age\"+@1 WHERE (\"LastName\" = @0)"));
332+
333+
db.UpdateIncrement(new Person { Age = -2, LastName = "Hendo" }, db.From<Person>().Update(x => new { x.Age, x.LastName }));
334+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"LastName\"=@0, \"Age\"=\"Age\"+@1"));
335+
336+
db.UpdateIncrement(new Person { Age = 10}, db.From<Person>().Update(x => x.Age).Where(x => x.FirstName == "JJ"));
337+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"Age\"=\"Age\"+@1 WHERE (\"FirstName\" = @0)"));
338+
327339
db.UpdateFmt<Person>(set: "FirstName = {0}".SqlFmt("JJ"), where: "LastName = {0}".SqlFmt("Hendrix"));
328340
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET FirstName = 'JJ' WHERE LastName = 'Hendrix'"));
329341

tests/ServiceStack.OrmLite.Tests/ApiSqliteTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,18 @@ public void API_Sqlite_Parameterized_Examples()
328328
db.UpdateOnly(new Person { FirstName = "JJ" }, q => q.Update(p => p.FirstName).Where(x => x.FirstName == "Jimi"));
329329
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"FirstName\"=@1 WHERE (\"FirstName\" = @0)"));
330330

331+
db.UpdateIncrement(new Person { Age = 3 }, x => x.Age);
332+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"Age\"=\"Age\"+@0"));
333+
334+
db.UpdateIncrement(new Person { Age = 5 }, x => x.Age, x => x.LastName == "Presley");
335+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"Age\"=\"Age\"+@1 WHERE (\"LastName\" = @0)"));
336+
337+
db.UpdateIncrement(new Person { Age = -2, LastName = "Hendo" }, db.From<Person>().Update(x => new { x.Age, x.LastName }));
338+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"LastName\"=@0, \"Age\"=\"Age\"+@1"));
339+
340+
db.UpdateIncrement(new Person { Age = 10 }, db.From<Person>().Update(x => x.Age).Where(x => x.FirstName == "JJ"));
341+
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET \"Age\"=\"Age\"+@1 WHERE (\"FirstName\" = @0)"));
342+
331343
db.UpdateFmt<Person>(set: "FirstName = {0}".SqlFmt("JJ"), where: "LastName = {0}".SqlFmt("Hendrix"));
332344
Assert.That(db.GetLastSql(), Is.EqualTo("UPDATE \"Person\" SET FirstName = 'JJ' WHERE LastName = 'Hendrix'"));
333345

0 commit comments

Comments
 (0)