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

Commit b67cf15

Browse files
committed
Add support for new Merge API to merge parent collections with their child references
1 parent 28b5a80 commit b67cf15

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

src/ServiceStack.OrmLite/Expressions/SqlExpression.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ public virtual SqlExpression<T> SelectDistinct<TKey>(Expression<Func<T, TKey>> f
132132
return this;
133133
}
134134

135+
public virtual SqlExpression<T> SelectDistinct()
136+
{
137+
selectDistinct = true;
138+
return this;
139+
}
140+
135141
public virtual SqlExpression<T> From(string tables)
136142
{
137143
if (tables != null)

src/ServiceStack.OrmLite/OrmLiteUtils.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.Collections;
1414
using System.Collections.Generic;
1515
using System.Data;
16+
using System.Linq;
1617
using System.Text;
1718
using ServiceStack.Text;
1819

@@ -348,5 +349,123 @@ public static ulong ConvertToULong(byte[] bytes)
348349
var ulongValue = BitConverter.ToUInt64(bytes, 0);
349350
return ulongValue;
350351
}
352+
353+
public static List<Parent> Merge<Parent, Child>(this Parent parent, List<Child> children)
354+
{
355+
return new List<Parent>{ parent}.Merge(children);
356+
}
357+
358+
public static List<Parent> Merge<Parent, Child>(this List<Parent> parents, List<Child> children)
359+
{
360+
var modelDef = ModelDefinition<Parent>.Definition;
361+
var fieldDef = modelDef.AllFieldDefinitionsArray.FirstOrDefault(
362+
x => (x.FieldType == typeof(Child) || x.FieldType == typeof(List<Child>)) && x.IsReference);
363+
364+
if (fieldDef == null)
365+
throw new Exception("Could not find Child Reference for '{0}' on Parent '{1}'".Fmt(typeof(Child).Name, typeof(Parent).Name));
366+
367+
var listInterface = fieldDef.FieldType.GetTypeWithGenericInterfaceOf(typeof(IList<>));
368+
if (listInterface != null)
369+
{
370+
var refType = listInterface.GenericTypeArguments()[0];
371+
var refModelDef = refType.GetModelDefinition();
372+
var refField = modelDef.GetRefFieldDef(refModelDef, refType);
373+
374+
SetListChildResults(parents, modelDef, fieldDef, refType, children, refField);
375+
}
376+
else
377+
{
378+
var refType = fieldDef.FieldType;
379+
380+
var refModelDef = refType.GetModelDefinition();
381+
382+
var refSelf = modelDef.GetSelfRefFieldDefIfExists(refModelDef, fieldDef);
383+
var refField = refSelf == null
384+
? modelDef.GetRefFieldDef(refModelDef, refType)
385+
: modelDef.GetRefFieldDefIfExists(refModelDef);
386+
387+
if (refSelf != null)
388+
{
389+
SetRefSelfChildResults(parents, fieldDef, refModelDef, refSelf, children);
390+
}
391+
else if (refField != null)
392+
{
393+
SetRefFieldChildResults(parents, modelDef, fieldDef, refField, children);
394+
}
395+
}
396+
397+
return parents;
398+
}
399+
400+
internal static void SetListChildResults<Parent>(List<Parent> parents, ModelDefinition modelDef,
401+
FieldDefinition fieldDef, Type refType, IList childResults, FieldDefinition refField)
402+
{
403+
var map = new Dictionary<object, List<object>>();
404+
List<object> refValues;
405+
406+
foreach (var result in childResults)
407+
{
408+
var refValue = refField.GetValue(result);
409+
if (!map.TryGetValue(refValue, out refValues))
410+
{
411+
map[refValue] = refValues = new List<object>();
412+
}
413+
refValues.Add(result);
414+
}
415+
416+
var untypedApi = refType.CreateTypedApi();
417+
foreach (var result in parents)
418+
{
419+
var pkValue = modelDef.PrimaryKey.GetValue(result);
420+
if (map.TryGetValue(pkValue, out refValues))
421+
{
422+
var castResults = untypedApi.Cast(refValues);
423+
fieldDef.SetValueFn(result, castResults);
424+
}
425+
}
426+
}
427+
428+
internal static void SetRefSelfChildResults<Parent>(List<Parent> parents, FieldDefinition fieldDef, ModelDefinition refModelDef, FieldDefinition refSelf, IList childResults)
429+
{
430+
var map = new Dictionary<object, object>();
431+
foreach (var result in childResults)
432+
{
433+
var pkValue = refModelDef.PrimaryKey.GetValue(result);
434+
map[pkValue] = result;
435+
}
436+
437+
foreach (var result in parents)
438+
{
439+
object childResult;
440+
var fkValue = refSelf.GetValue(result);
441+
if (fkValue != null && map.TryGetValue(fkValue, out childResult))
442+
{
443+
fieldDef.SetValueFn(result, childResult);
444+
}
445+
}
446+
}
447+
448+
internal static void SetRefFieldChildResults<Parent>(List<Parent> parents, ModelDefinition modelDef,
449+
FieldDefinition fieldDef, FieldDefinition refField, IList childResults)
450+
{
451+
var map = new Dictionary<object, object>();
452+
453+
foreach (var result in childResults)
454+
{
455+
var refValue = refField.GetValue(result);
456+
map[refValue] = result;
457+
}
458+
459+
foreach (var result in parents)
460+
{
461+
object childResult;
462+
var pkValue = modelDef.PrimaryKey.GetValue(result);
463+
if (map.TryGetValue(pkValue, out childResult))
464+
{
465+
fieldDef.SetValueFn(result, childResult);
466+
}
467+
}
468+
}
469+
351470
}
352471
}

src/ServiceStack.OrmLite/UntypedApi.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ public static IUntypedApi CreateTypedApi(this IDbCommand dbCmd, Type forType)
2929
unTypedApi.DbCmd = dbCmd;
3030
return unTypedApi;
3131
}
32+
33+
public static IUntypedApi CreateTypedApi(this Type forType)
34+
{
35+
var genericType = untypedApiMap.GetOrAdd(forType, key => typeof(UntypedApi<>).MakeGenericType(key));
36+
var unTypedApi = genericType.CreateInstance<IUntypedApi>();
37+
return unTypedApi;
38+
}
3239
}
3340

3441
public class UntypedApi<T> : IUntypedApi

tests/ServiceStack.OrmLite.Tests/LoadReferencesJoinTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,29 @@ public void Can_load_list_of_references()
551551
Is.EquivalentTo(new[] { 1.99m, 3.98m, 1.49m, 2.98m, 9.99m }));
552552
}
553553

554+
[Test]
555+
public void Can_load_list_of_references_using_subselect()
556+
{
557+
AddCustomersWithOrders();
558+
559+
var customers = db.Select<Customer>(q =>
560+
q.Join<Order>()
561+
.Where<Order>(o => o.Qty == 1)
562+
.SelectDistinct());
563+
564+
var orders = db.Select<Order>(o => o.Qty == 1);
565+
566+
customers.Merge(orders);
567+
568+
customers.PrintDump();
569+
570+
Assert.That(customers.Count, Is.EqualTo(2));
571+
Assert.That(customers[0].Orders.Count, Is.EqualTo(3));
572+
Assert.That(customers[0].Orders.All(x => x.Qty == 1));
573+
Assert.That(customers[1].Orders.Count, Is.EqualTo(1));
574+
Assert.That(customers[1].Orders.All(x => x.Qty == 1));
575+
}
576+
554577
[Test]
555578
public void Can_join_on_references_attribute()
556579
{

0 commit comments

Comments
 (0)