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

Commit 80afdc2

Browse files
committed
Add support for batch loading of references in multi select queries.
Only adds 1 additional query per reference.
1 parent 70aaec1 commit 80afdc2

File tree

5 files changed

+317
-52
lines changed

5 files changed

+317
-52
lines changed

src/ServiceStack.OrmLite/Expressions/ReadConnectionExtensions.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,5 +217,34 @@ public static long Count<T>(this IDbConnection dbConn)
217217
var expression = OrmLiteConfig.DialectProvider.SqlExpression<T>();
218218
return dbConn.Exec(dbCmd => dbCmd.Count(expression));
219219
}
220+
221+
222+
/// <summary>
223+
/// Returns results with references from using a LINQ Expression. E.g:
224+
/// <para>db.Load&lt;Person&gt;(x =&gt; x.Age &gt; 40)</para>
225+
/// </summary>
226+
public static List<T> LoadSelect<T>(this IDbConnection dbConn, Expression<Func<T, bool>> predicate)
227+
{
228+
return dbConn.Exec(dbCmd => dbCmd.LoadSelect(predicate));
229+
}
230+
231+
/// <summary>
232+
/// Returns results with references from using an SqlExpression lambda. E.g:
233+
/// <para>db.Load&lt;Person&gt;(q =&gt; q.Where(x =&gt; x.Age &gt; 40))</para>
234+
/// </summary>
235+
public static List<T> LoadSelect<T>(this IDbConnection dbConn, Func<SqlExpression<T>, SqlExpression<T>> expression)
236+
{
237+
return dbConn.Exec(dbCmd => dbCmd.LoadSelect(expression));
238+
}
239+
240+
/// <summary>
241+
/// Returns results with references from using an SqlExpression lambda. E.g:
242+
/// <para>db.Load(db.From&lt;Person&gt;().Where(x =&gt; x.Age &gt; 40))</para>
243+
/// </summary>
244+
public static List<T> LoadSelect<T>(this IDbConnection dbConn, SqlExpression<T> expression = null)
245+
{
246+
return dbConn.Exec(dbCmd => dbCmd.LoadSelect(expression));
247+
}
248+
220249
}
221250
}

src/ServiceStack.OrmLite/Expressions/ReadExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Data;
55
using System.Linq.Expressions;
6+
using System.Text;
67

78
namespace ServiceStack.OrmLite
89
{
@@ -115,6 +116,24 @@ internal static long Count<T>(this IDbCommand dbCmd, Expression<Func<T, bool>> p
115116
return dbCmd.Scalar<long>(sql);
116117
}
117118

119+
internal static List<T> LoadSelect<T>(this IDbCommand dbCmd, Func<SqlExpression<T>, SqlExpression<T>> expression)
120+
{
121+
var expr = OrmLiteConfig.DialectProvider.SqlExpression<T>();
122+
expr = expression(expr);
123+
return dbCmd.LoadListWithReferences(expr);
124+
}
125+
126+
internal static List<T> LoadSelect<T>(this IDbCommand dbCmd, SqlExpression<T> expression = null)
127+
{
128+
return dbCmd.LoadListWithReferences(expression);
129+
}
130+
131+
internal static List<T> LoadSelect<T>(this IDbCommand dbCmd, Expression<Func<T, bool>> predicate)
132+
{
133+
var expr = OrmLiteConfig.DialectProvider.SqlExpression<T>().Where(predicate);
134+
return dbCmd.LoadListWithReferences(expr);
135+
}
136+
118137
internal static T ExprConvertTo<T>(this IDataReader dataReader)
119138
{
120139
var fieldDefs = ModelDefinition<T>.Definition.AllFieldDefinitionsArray;

src/ServiceStack.OrmLite/OrmLiteReadExtensions.cs

Lines changed: 136 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ public static void LoadReferences<T>(this IDbCommand dbCmd, T instance)
847847
var modelDef = ModelDefinition<T>.Definition;
848848
var fieldDefs = modelDef.AllFieldDefinitionsArray.Where(x => x.IsReference);
849849
var pkValue = modelDef.PrimaryKey.GetValue(instance);
850-
var ormLiteDialectProvider = OrmLiteConfig.DialectProvider;
850+
var dialectProvider = OrmLiteConfig.DialectProvider;
851851

852852
foreach (var fieldDef in fieldDefs)
853853
{
@@ -860,8 +860,8 @@ public static void LoadReferences<T>(this IDbCommand dbCmd, T instance)
860860

861861
var refField = GetRefFieldDef(modelDef, refModelDef, refType);
862862

863-
var sqlFilter = ormLiteDialectProvider.GetQuotedColumnName(refField.FieldName) + "={0}";
864-
var sql = ormLiteDialectProvider.ToSelectStatement(refType, sqlFilter, pkValue);
863+
var sqlFilter = dialectProvider.GetQuotedColumnName(refField.FieldName) + "={0}";
864+
var sql = dialectProvider.ToSelectStatement(refType, sqlFilter, pkValue);
865865

866866
var results = dbCmd.ConvertToList(refType, sql);
867867
fieldDef.SetValueFn(instance, results);
@@ -878,24 +878,153 @@ public static void LoadReferences<T>(this IDbCommand dbCmd, T instance)
878878

879879
if (refField != null)
880880
{
881-
var sqlFilter = ormLiteDialectProvider.GetQuotedColumnName(refField.FieldName) + "={0}";
882-
var sql = ormLiteDialectProvider.ToSelectStatement(refType, sqlFilter, pkValue);
881+
var sqlFilter = dialectProvider.GetQuotedColumnName(refField.FieldName) + "={0}";
882+
var sql = dialectProvider.ToSelectStatement(refType, sqlFilter, pkValue);
883883
var result = dbCmd.ConvertTo(refType, sql);
884884
fieldDef.SetValueFn(instance, result);
885885
}
886886
else if (refSelf != null)
887887
{
888888
//Load Self Table.RefTableId PK
889889
var refPkValue = refSelf.GetValue(instance);
890-
var sqlFilter = ormLiteDialectProvider.GetQuotedColumnName(refModelDef.PrimaryKey.FieldName) + "={0}";
891-
var sql = ormLiteDialectProvider.ToSelectStatement(refType, sqlFilter, refPkValue);
890+
var sqlFilter = dialectProvider.GetQuotedColumnName(refModelDef.PrimaryKey.FieldName) + "={0}";
891+
var sql = dialectProvider.ToSelectStatement(refType, sqlFilter, refPkValue);
892892
var result = dbCmd.ConvertTo(refType, sql);
893893
fieldDef.SetValueFn(instance, result);
894894
}
895895
}
896896
}
897897
}
898898

899+
internal static List<T> LoadListWithReferences<T>(this IDbCommand dbCmd, SqlExpression<T> expr = null)
900+
{
901+
var dialectProvider = OrmLiteConfig.DialectProvider;
902+
if (expr == null)
903+
expr = dialectProvider.SqlExpression<T>();
904+
905+
var sql = expr.SelectInto<T>();
906+
var parentResults = dbCmd.ExprConvertToList<T>(sql);
907+
908+
var modelDef = ModelDefinition<T>.Definition;
909+
var fieldDefs = modelDef.AllFieldDefinitionsArray.Where(x => x.IsReference);
910+
911+
expr.Select(dialectProvider.GetQuotedColumnName(modelDef.PrimaryKey));
912+
var subSql = expr.ToSelectStatement();
913+
914+
foreach (var fieldDef in fieldDefs)
915+
{
916+
var listInterface = fieldDef.FieldType.GetTypeWithGenericInterfaceOf(typeof(IList<>));
917+
if (listInterface != null)
918+
{
919+
var refType = listInterface.GenericTypeArguments()[0];
920+
var refModelDef = refType.GetModelDefinition();
921+
922+
var refField = GetRefFieldDef(modelDef, refModelDef, refType);
923+
924+
var sqlRef = "SELECT {0} FROM {1} WHERE {2} IN ({3})".Fmt(
925+
dialectProvider.GetColumnNames(refModelDef),
926+
dialectProvider.GetQuotedTableName(refModelDef),
927+
dialectProvider.GetQuotedColumnName(refField),
928+
subSql);
929+
var childResults = dbCmd.ConvertToList(refType, sqlRef);
930+
931+
var map = new Dictionary<object, List<object>>();
932+
List<object> refValues;
933+
934+
foreach (var result in childResults)
935+
{
936+
var refValue = refField.GetValue(result);
937+
if (!map.TryGetValue(refValue, out refValues))
938+
{
939+
map[refValue] = refValues = new List<object>();
940+
}
941+
refValues.Add(result);
942+
}
943+
944+
var untypedApi = dbCmd.CreateTypedApi(refType);
945+
foreach (var result in parentResults)
946+
{
947+
var pkValue = modelDef.PrimaryKey.GetValue(result);
948+
if (map.TryGetValue(pkValue, out refValues))
949+
{
950+
var castResults = untypedApi.Cast(refValues);
951+
fieldDef.SetValueFn(result, castResults);
952+
}
953+
}
954+
}
955+
else
956+
{
957+
var refType = fieldDef.FieldType;
958+
var refModelDef = refType.GetModelDefinition();
959+
960+
var refSelf = GetSelfRefFieldDefIfExists(modelDef, refModelDef);
961+
var refField = refSelf == null
962+
? GetRefFieldDef(modelDef, refModelDef, refType)
963+
: GetRefFieldDefIfExists(modelDef, refModelDef);
964+
965+
var map = new Dictionary<object, object>();
966+
967+
if (refField != null)
968+
{
969+
var sqlRef = "SELECT {0} FROM {1} WHERE {2} IN ({3})".Fmt(
970+
dialectProvider.GetColumnNames(refModelDef),
971+
dialectProvider.GetQuotedTableName(refModelDef),
972+
dialectProvider.GetQuotedColumnName(refField),
973+
subSql);
974+
var childResults = dbCmd.ConvertToList(refType, sqlRef);
975+
976+
foreach (var result in childResults)
977+
{
978+
var refValue = refField.GetValue(result);
979+
map[refValue] = result;
980+
}
981+
982+
foreach (var result in parentResults)
983+
{
984+
object childResult;
985+
var pkValue = modelDef.PrimaryKey.GetValue(result);
986+
if (map.TryGetValue(pkValue, out childResult))
987+
{
988+
fieldDef.SetValueFn(result, childResult);
989+
}
990+
}
991+
}
992+
else if (refSelf != null)
993+
{
994+
//Load Self Table.RefTableId PK
995+
expr.Select(dialectProvider.GetQuotedColumnName(refSelf));
996+
subSql = expr.ToSelectStatement();
997+
998+
var sqlRef = "SELECT {0} FROM {1} WHERE {2} IN ({3})".Fmt(
999+
dialectProvider.GetColumnNames(refModelDef),
1000+
dialectProvider.GetQuotedTableName(refModelDef),
1001+
dialectProvider.GetQuotedColumnName(refModelDef.PrimaryKey),
1002+
subSql);
1003+
var childResults = dbCmd.ConvertToList(refType, sqlRef);
1004+
1005+
foreach (var result in childResults)
1006+
{
1007+
var pkValue = refModelDef.PrimaryKey.GetValue(result);
1008+
map[pkValue] = result;
1009+
}
1010+
1011+
foreach (var result in parentResults)
1012+
{
1013+
object childResult;
1014+
var pkValue = modelDef.PrimaryKey.GetValue(result);
1015+
if (map.TryGetValue(pkValue, out childResult))
1016+
{
1017+
fieldDef.SetValueFn(result, childResult);
1018+
}
1019+
}
1020+
}
1021+
}
1022+
1023+
}
1024+
1025+
return parentResults;
1026+
}
1027+
8991028
public static FieldDefinition GetRefFieldDef(ModelDefinition modelDef, ModelDefinition refModelDef, Type refType)
9001029
{
9011030
var refField = GetRefFieldDefIfExists(modelDef, refModelDef);

0 commit comments

Comments
 (0)