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

Commit 8cee8a2

Browse files
committed
Add support for auto splitting of IEnumerable params into multi db params
1 parent 742f9ae commit 8cee8a2

File tree

5 files changed

+84
-34
lines changed

5 files changed

+84
-34
lines changed

src/ServiceStack.OrmLite/Async/OrmLiteReadCommandExtensionsAsync.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ internal static Task<List<TModel>> SelectAsync<TModel>(this IDbCommand dbCmd, Ty
5656

5757
internal static Task<List<TModel>> SelectAsync<TModel>(this IDbCommand dbCmd, Type fromTableType, string sqlFilter, object anonType, CancellationToken token)
5858
{
59-
if (anonType != null) dbCmd.SetParameters(fromTableType, anonType, excludeDefaults: false);
59+
if (anonType != null) dbCmd.SetParameters(fromTableType, anonType, excludeDefaults: false, sql: ref sqlFilter);
6060
var sql = OrmLiteReadCommandExtensions.ToSelect<TModel>(dbCmd.GetDialectProvider(), fromTableType, sqlFilter);
6161
return dbCmd.ConvertToListAsync<TModel>(sql, token);
6262
}
@@ -105,7 +105,7 @@ internal static Task<T> SingleAsync<T>(this IDbCommand dbCmd, string sql, object
105105
{
106106
return OrmLiteUtils.IsScalar<T>()
107107
? dbCmd.ScalarAsync<T>(sql, anonType, token)
108-
: dbCmd.SetParameters<T>(anonType, excludeDefaults: false).ConvertToAsync<T>(dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql), token);
108+
: dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).ConvertToAsync<T>(dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql), token);
109109
}
110110

111111
internal static Task<List<T>> WhereAsync<T>(this IDbCommand dbCmd, string name, object value, CancellationToken token)
@@ -131,7 +131,7 @@ internal static Task<List<T>> SelectAsync<T>(this IDbCommand dbCmd, string sql,
131131

132132
internal static Task<List<T>> SelectAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
133133
{
134-
dbCmd.SetParameters<T>(anonType, excludeDefaults: false).CommandText = dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql);
134+
dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).CommandText = dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql);
135135
return dbCmd.ConvertToListAsync<T>(null, token);
136136
}
137137

@@ -149,7 +149,7 @@ internal static Task<List<T>> SqlListAsync<T>(this IDbCommand dbCmd, string sql,
149149

150150
internal static Task<List<T>> SqlListAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
151151
{
152-
dbCmd.SetParameters<T>(anonType, excludeDefaults: false).CommandText = sql;
152+
dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).CommandText = sql;
153153
return dbCmd.ConvertToListAsync<T>(null, token);
154154
}
155155

@@ -192,7 +192,7 @@ internal static Task<T> SqlScalarAsync<T>(this IDbCommand dbCmd, string sql, IEn
192192

193193
internal static Task<T> SqlScalarAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
194194
{
195-
return dbCmd.SetParameters<T>(anonType, excludeDefaults: false).ScalarAsync<T>(sql, token);
195+
return dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).ScalarAsync<T>(sql, token);
196196
}
197197

198198
internal static Task<T> SqlScalarAsync<T>(this IDbCommand dbCmd, string sql, Dictionary<string, object> dict, CancellationToken token)
@@ -207,12 +207,12 @@ internal static Task<List<T>> SelectNonDefaultsAsync<T>(this IDbCommand dbCmd, o
207207

208208
internal static Task<List<T>> SelectNonDefaultsAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
209209
{
210-
return dbCmd.SetParameters<T>(anonType, excludeDefaults: true).ConvertToListAsync<T>(dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql), token);
210+
return dbCmd.SetParameters<T>(anonType, excludeDefaults: true, sql: ref sql).ConvertToListAsync<T>(dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql), token);
211211
}
212212

213213
internal static Task<T> ScalarAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
214214
{
215-
return dbCmd.SetParameters<T>(anonType, excludeDefaults: false).ScalarAsync<T>(sql, token);
215+
return dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).ScalarAsync<T>(sql, token);
216216
}
217217

218218
internal static Task<T> ScalarAsync<T>(this IDataReader reader, IOrmLiteDialectProvider dialectProvider, CancellationToken token)
@@ -229,7 +229,7 @@ public static Task<long> LongScalarAsync(this IDbCommand dbCmd, CancellationToke
229229

230230
internal static Task<List<T>> ColumnAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
231231
{
232-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
232+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
233233

234234
return dbCmd.ColumnAsync<T>(dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql), token);
235235
}
@@ -251,7 +251,7 @@ internal static Task<List<T>> ColumnAsync<T>(this IDataReader reader, IOrmLiteDi
251251

252252
internal static Task<HashSet<T>> ColumnDistinctAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
253253
{
254-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
254+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
255255

256256
return dbCmd.ColumnDistinctAsync<T>(sql, token);
257257
}

src/ServiceStack.OrmLite/Async/OrmLiteWriteCommandExtensionsAsync.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,13 @@ internal static Task<int> DeleteAllAsync(this IDbCommand dbCmd, Type tableType,
252252

253253
internal static Task<int> DeleteAsync<T>(this IDbCommand dbCmd, string sql, object anonType, CancellationToken token)
254254
{
255-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
255+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
256256
return dbCmd.ExecuteSqlAsync(dbCmd.GetDialectProvider().ToDeleteStatement(typeof(T), sql), token);
257257
}
258258

259259
internal static Task<int> DeleteAsync(this IDbCommand dbCmd, Type tableType, string sql, object anonType, CancellationToken token)
260260
{
261-
if (anonType != null) dbCmd.SetParameters(tableType, anonType, excludeDefaults: false);
261+
if (anonType != null) dbCmd.SetParameters(tableType, anonType, excludeDefaults: false, sql: ref sql);
262262
return dbCmd.ExecuteSqlAsync(dbCmd.GetDialectProvider().ToDeleteStatement(tableType, sql), token);
263263
}
264264

src/ServiceStack.OrmLite/OrmLiteReadCommandExtensions.cs

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,27 @@ internal static void SetFilter<T>(this IDbCommand dbCmd, string name, object val
8080

8181
internal static IDbCommand SetFilters<T>(this IDbCommand dbCmd, object anonType, bool excludeDefaults)
8282
{
83-
dbCmd.SetParameters<T>(anonType, excludeDefaults); //needs to be called first
83+
string ignore = null;
84+
dbCmd.SetParameters<T>(anonType, excludeDefaults, ref ignore); //needs to be called first
8485
dbCmd.CommandText = dbCmd.GetFilterSql<T>();
8586
return dbCmd;
8687
}
8788

88-
internal static IDbCommand SetParameters<T>(this IDbCommand dbCmd, object anonType, bool excludeDefaults)
89+
internal static IDbCommand SetParameters<T>(this IDbCommand dbCmd, object anonType, bool excludeDefaults, ref string sql) =>
90+
dbCmd.SetParameters(typeof(T), anonType, excludeDefaults, ref sql);
91+
92+
private static IEnumerable GetMultiValues(object value)
8993
{
90-
return dbCmd.SetParameters(typeof(T), anonType, excludeDefaults);
94+
if (value is SqlInValues inValues)
95+
return inValues.GetValues();
96+
97+
return (value is IEnumerable enumerable &&
98+
!(enumerable is string ||
99+
enumerable is IEnumerable<KeyValuePair<string, object>>)
100+
) ? enumerable : null;
91101
}
92102

93-
internal static IDbCommand SetParameters(this IDbCommand dbCmd, Type type, object anonType, bool excludeDefaults)
103+
internal static IDbCommand SetParameters(this IDbCommand dbCmd, Type type, object anonType, bool excludeDefaults, ref string sql)
94104
{
95105
if (anonType == null)
96106
return dbCmd;
@@ -104,16 +114,25 @@ internal static IDbCommand SetParameters(this IDbCommand dbCmd, Type type, objec
104114
? dialectProvider.GetFieldDefinitionMap(modelDef)
105115
: null;
106116

117+
var sqlCopy = sql; //C# doesn't allow changing ref params in lambda's
118+
107119
anonType.ToObjectDictionary().ForEachParam(modelDef, excludeDefaults, (propName, columnName, value) =>
108120
{
109121
var propType = value?.GetType() ?? typeof(object);
110-
if (value is SqlInValues inValues)
122+
var inValues = GetMultiValues(value);
123+
if (inValues != null)
111124
{
112125
var i = 0;
113-
foreach (var item in inValues.GetValues())
126+
var sb = StringBuilderCache.Allocate();
127+
foreach (var item in inValues)
114128
{
115129
var p = dbCmd.CreateParameter();
116130
p.ParameterName = "v" + i++;
131+
132+
if (sb.Length > 0)
133+
sb.Append(',');
134+
sb.Append(dialectProvider.ParamString + p.ParameterName);
135+
117136
p.Direction = ParameterDirection.Input;
118137
dialectProvider.InitDbParam(p, item.GetType());
119138

@@ -130,6 +149,9 @@ internal static IDbCommand SetParameters(this IDbCommand dbCmd, Type type, objec
130149

131150
dbCmd.Parameters.Add(p);
132151
}
152+
153+
var sqlIn = StringBuilderCache.ReturnAndFree(sb);
154+
sqlCopy = sqlCopy?.Replace(dialectProvider.ParamString + propName, sqlIn);
133155
}
134156
else
135157
{
@@ -155,14 +177,15 @@ internal static IDbCommand SetParameters(this IDbCommand dbCmd, Type type, objec
155177

156178
p.Value = value == null ?
157179
DBNull.Value
158-
: p.DbType == DbType.String ?
159-
value.ToString() :
160-
value;
180+
: p.DbType == DbType.String ?
181+
value.ToString() :
182+
value;
161183

162184
dbCmd.Parameters.Add(p);
163185
}
164186
});
165187

188+
sql = sqlCopy;
166189
return dbCmd;
167190
}
168191

@@ -337,7 +360,7 @@ internal static T Single<T>(this IDbCommand dbCmd, string sql, IEnumerable<IDbDa
337360

338361
internal static T Single<T>(this IDbCommand dbCmd, string sql, object anonType)
339362
{
340-
dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
363+
dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
341364

342365
return OrmLiteUtils.IsScalar<T>()
343366
? dbCmd.Scalar<T>(sql)
@@ -371,7 +394,7 @@ internal static List<T> Select<T>(this IDbCommand dbCmd, string sql, IEnumerable
371394

372395
internal static List<T> Select<T>(this IDbCommand dbCmd, string sql, object anonType = null)
373396
{
374-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
397+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
375398
dbCmd.CommandText = dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql);
376399

377400
return dbCmd.ConvertToList<T>();
@@ -392,7 +415,7 @@ internal static List<TModel> Select<TModel>(this IDbCommand dbCmd, Type fromTabl
392415

393416
internal static List<T> Select<T>(this IDbCommand dbCmd, Type fromTableType, string sql, object anonType = null)
394417
{
395-
if (anonType != null) dbCmd.SetParameters(fromTableType, anonType, excludeDefaults: false);
418+
if (anonType != null) dbCmd.SetParameters(fromTableType, anonType, excludeDefaults: false, sql: ref sql);
396419
dbCmd.CommandText = ToSelect<T>(dbCmd.GetDialectProvider(), fromTableType, sql);
397420

398421
return dbCmd.ConvertToList<T>();
@@ -423,7 +446,7 @@ internal static List<T> SqlList<T>(this IDbCommand dbCmd, string sql, IEnumerabl
423446

424447
internal static List<T> SqlList<T>(this IDbCommand dbCmd, string sql, object anonType = null)
425448
{
426-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
449+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
427450
dbCmd.CommandText = sql;
428451

429452
return dbCmd.ConvertToList<T>();
@@ -453,7 +476,7 @@ internal static List<T> SqlColumn<T>(this IDbCommand dbCmd, string sql, IEnumera
453476

454477
internal static List<T> SqlColumn<T>(this IDbCommand dbCmd, string sql, object anonType = null)
455478
{
456-
dbCmd.SetParameters<T>(anonType, excludeDefaults: false).CommandText = sql;
479+
dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).CommandText = sql;
457480
return dbCmd.ConvertToList<T>();
458481
}
459482

@@ -472,7 +495,7 @@ internal static T SqlScalar<T>(this IDbCommand dbCmd, string sql, IEnumerable<ID
472495

473496
internal static T SqlScalar<T>(this IDbCommand dbCmd, string sql, object anonType = null)
474497
{
475-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
498+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
476499

477500
return dbCmd.Scalar<T>(sql);
478501
}
@@ -493,7 +516,7 @@ internal static List<T> SelectNonDefaults<T>(this IDbCommand dbCmd, object filte
493516

494517
internal static List<T> SelectNonDefaults<T>(this IDbCommand dbCmd, string sql, object anonType = null)
495518
{
496-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: true);
519+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: true, sql: ref sql);
497520

498521
return dbCmd.ConvertToList<T>(dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql));
499522
}
@@ -505,7 +528,7 @@ internal static IEnumerable<T> SelectLazy<T>(this IDbCommand dbCmd, string sql,
505528

506529
internal static IEnumerable<T> SelectLazy<T>(this IDbCommand dbCmd, string sql, object anonType = null)
507530
{
508-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
531+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
509532
var dialectProvider = dbCmd.GetDialectProvider();
510533
dbCmd.CommandText = dialectProvider.ToSelectStatement(typeof(T), sql);
511534

@@ -539,7 +562,7 @@ internal static IEnumerable<T> ColumnLazy<T>(this IDbCommand dbCmd, string sql,
539562

540563
internal static IEnumerable<T> ColumnLazy<T>(this IDbCommand dbCmd, string sql, object anonType)
541564
{
542-
foreach (var p in dbCmd.SetParameters<T>(anonType, excludeDefaults: false).ColumnLazy<T>(sql)) yield return p;
565+
foreach (var p in dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).ColumnLazy<T>(sql)) yield return p;
543566
}
544567

545568
private static IEnumerable<T> ColumnLazy<T>(this IDbCommand dbCmd, string sql)
@@ -603,7 +626,7 @@ internal static IEnumerable<T> SelectLazy<T>(this IDbCommand dbCmd)
603626

604627
internal static T Scalar<T>(this IDbCommand dbCmd, string sql, object anonType = null)
605628
{
606-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
629+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
607630

608631
return dbCmd.Scalar<T>(sql);
609632
}
@@ -656,7 +679,7 @@ internal static long LastInsertId(this IDbCommand dbCmd)
656679

657680
internal static List<T> Column<T>(this IDbCommand dbCmd, string sql, object anonType = null)
658681
{
659-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
682+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
660683

661684
return dbCmd.Column<T>(dbCmd.GetDialectProvider().ToSelectStatement(typeof(T), sql));
662685
}
@@ -683,7 +706,7 @@ internal static HashSet<T> ColumnDistinct<T>(this IDbCommand dbCmd, string sql,
683706

684707
internal static HashSet<T> ColumnDistinct<T>(this IDbCommand dbCmd, string sql, object anonType = null)
685708
{
686-
return dbCmd.SetParameters<T>(anonType, excludeDefaults: false).ColumnDistinct<T>(sql);
709+
return dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql).ColumnDistinct<T>(sql);
687710
}
688711

689712
internal static HashSet<T> ColumnDistinct<T>(this IDataReader reader, IOrmLiteDialectProvider dialectProvider)

src/ServiceStack.OrmLite/OrmLiteWriteCommandExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,13 +618,13 @@ internal static int DeleteAll(this IDbCommand dbCmd, Type tableType)
618618

619619
internal static int Delete<T>(this IDbCommand dbCmd, string sql, object anonType = null)
620620
{
621-
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false);
621+
if (anonType != null) dbCmd.SetParameters<T>(anonType, excludeDefaults: false, sql: ref sql);
622622
return dbCmd.ExecuteSql(dbCmd.GetDialectProvider().ToDeleteStatement(typeof(T), sql));
623623
}
624624

625625
internal static int Delete(this IDbCommand dbCmd, Type tableType, string sql, object anonType = null)
626626
{
627-
if (anonType != null) dbCmd.SetParameters(tableType, anonType, excludeDefaults: false);
627+
if (anonType != null) dbCmd.SetParameters(tableType, anonType, excludeDefaults: false, sql: ref sql);
628628
return dbCmd.ExecuteSql(dbCmd.GetDialectProvider().ToDeleteStatement(tableType, sql));
629629
}
630630

tests/ServiceStack.OrmLite.Tests/OrmLiteSelectTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using NUnit.Framework;
66
using ServiceStack.Common;
77
using ServiceStack.Common.Tests.Models;
8+
using ServiceStack.Logging;
89
using ServiceStack.Text;
910

1011
namespace ServiceStack.OrmLite.Tests
@@ -331,11 +332,37 @@ public void Can_Select_In_for_string_value()
331332
var rows = db.Select<ModelWithIdAndName>("Name IN ({0})".Fmt(selectInNames.SqlInParams()),
332333
new { values = selectInNames.SqlInValues() });
333334
Assert.That(rows.Count, Is.EqualTo(selectInNames.Length));
335+
336+
rows = db.Select<ModelWithIdAndName>("Name IN (@values)",
337+
new { values = selectInNames });
338+
Assert.That(rows.Count, Is.EqualTo(selectInNames.Length));
339+
334340
rows = db.Select<ModelWithIdAndName>("Name IN (@p1, @p2)".PreNormalizeSql(db), new { p1 = "Name1", p2 = "Name2" });
335341
Assert.That(rows.Count, Is.EqualTo(selectInNames.Length));
336342
}
337343
}
338344

345+
[Test]
346+
public void Can_select_IN_using_array_or_List_params()
347+
{
348+
LogManager.LogFactory = new ConsoleLogFactory();
349+
using (var db = OpenDbConnection())
350+
{
351+
db.DropAndCreateTable<ModelWithIdAndName>();
352+
5.Times(x => db.Insert(ModelWithIdAndName.Create(x)));
353+
354+
var names = new[] { "Name2", "Name3" };
355+
var rows = db.Select<ModelWithIdAndName>("Name IN (@names)", new { names });
356+
Assert.That(rows.Count, Is.EqualTo(2));
357+
Assert.That(rows.Map(x => x.Name), Is.EquivalentTo(names));
358+
359+
var ids = new List<int> { 2, 3 };
360+
rows = db.Select<ModelWithIdAndName>("Id IN (@ids)", new { ids });
361+
Assert.That(rows.Count, Is.EqualTo(2));
362+
Assert.That(rows.Map(x => x.Id), Is.EquivalentTo(ids));
363+
}
364+
}
365+
339366
public class PocoFlag
340367
{
341368
public string Name { get; set; }

0 commit comments

Comments
 (0)