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

Commit dc2d6f1

Browse files
committed
Merge pull request #499 from madaleno/UpdateIncrement
Implemented an UpdateIncrement API
2 parents 130b4f2 + cf1d0f4 commit dc2d6f1

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)