Skip to content

Commit 3aee86b

Browse files
committed
With differentiates master and detail relationships
1 parent 712aab4 commit 3aee86b

File tree

8 files changed

+256
-146
lines changed

8 files changed

+256
-146
lines changed

Simple.Data.Ado/EagerLoadingEnumerable.cs

Lines changed: 63 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,7 @@ IEnumerator IEnumerable.GetEnumerator()
3838

3939
private IEnumerable<IDictionary<string,object>> CreateObjectGraphs()
4040
{
41-
var load = new Dictionary<IDictionary<string,object>,IDictionary<string, HashSet<IDictionary<string, object>>>>(new DictionaryEqualityComparer());
42-
43-
foreach (var dict in _source)
44-
{
45-
IDictionary<string, HashSet<IDictionary<string, object>>> children;
46-
47-
var main = new SubDictionary<string, object>(dict, s => !s.StartsWith("__with__"));
48-
if (!load.TryGetValue(main, out children))
49-
{
50-
children = new Dictionary<string, HashSet<IDictionary<string, object>>>();
51-
load.Add(main, children);
52-
}
53-
54-
foreach (var tuple in ExtractObjects(dict))
55-
{
56-
if (children.ContainsKey(tuple.Item1))
57-
{
58-
children[tuple.Item1].Add(tuple.Item2);
59-
}
60-
else
61-
{
62-
children.Add(tuple.Item1, new HashSet<IDictionary<string, object>>(new DictionaryEqualityComparer()) {tuple.Item2});
63-
}
64-
}
65-
}
41+
var load = BuildLoadDictionary();
6642

6743
IDictionary<string,int> index = null;
6844
foreach (var kvp in load)
@@ -74,128 +50,94 @@ private IEnumerable<IDictionary<string,object>> CreateObjectGraphs()
7450
var row = new OptimizedDictionary<string, object>(index, kvp.Key.Values);
7551
foreach (var sub in kvp.Value)
7652
{
77-
if (sub.Value.Count == 1)
53+
if (sub.Value.Single != null)
7854
{
79-
kvp.Key[sub.Key] = sub.Value.Single();
55+
row[sub.Key] = sub.Value.Single;
8056
}
81-
else
57+
else if (sub.Value.Collection != null)
8258
{
83-
kvp.Key[sub.Key] = sub.Value.ToList();
59+
row[sub.Key] = sub.Value.Collection.ToList();
8460
}
8561
}
8662

87-
yield return kvp.Key;
63+
yield return row;
8864
}
8965
}
9066

91-
private IEnumerable<Tuple<string,Dictionary<string,object>>> ExtractObjects(IDictionary<string,object> source)
92-
{
93-
var names =
94-
source.Keys.Where(k => k.StartsWith("__with__")).Select(
95-
k => k.Split(new[] {"__"}, StringSplitOptions.RemoveEmptyEntries)[1]).ToList();
96-
97-
return from name in names
98-
let pattern = "__with__" + name + "__"
99-
select Tuple.Create(name, source.Where(kvp => kvp.Key.StartsWith(pattern))
100-
.ToDictionary(kvp => kvp.Key.Replace(pattern, ""), kvp => kvp.Value));
101-
}
102-
}
103-
104-
internal class SubDictionary<TKey,TValue> : IDictionary<TKey,TValue>
105-
{
106-
private readonly IDictionary<TKey, TValue> _super;
107-
private readonly Func<TKey, bool> _keyFilter;
108-
109-
private IEnumerable<KeyValuePair<TKey, TValue>> Filter()
110-
{
111-
return _super.Where(kvp => _keyFilter(kvp.Key));
112-
}
113-
114-
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
115-
{
116-
return Filter().GetEnumerator();
117-
}
118-
119-
public void Add(KeyValuePair<TKey, TValue> item)
120-
{
121-
_super.Add(item);
122-
}
123-
124-
public void Clear()
125-
{
126-
_super.Clear();
127-
}
128-
129-
public bool Contains(KeyValuePair<TKey, TValue> item)
130-
{
131-
return _super.Contains(item);
132-
}
133-
134-
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
135-
{
136-
Filter().ToArray().CopyTo(array, arrayIndex);
137-
}
138-
139-
public bool Remove(KeyValuePair<TKey, TValue> item)
67+
private Dictionary<IDictionary<string, object>, IDictionary<string, WithContainer>> BuildLoadDictionary()
14068
{
141-
return _super.Remove(item);
142-
}
69+
var load =
70+
new Dictionary<IDictionary<string, object>, IDictionary<string, WithContainer>>(
71+
new DictionaryEqualityComparer());
14372

144-
public int Count
145-
{
146-
get { return Filter().Count(); }
147-
}
73+
foreach (var dict in _source)
74+
{
75+
IDictionary<string, WithContainer> withContainers;
14876

149-
public bool IsReadOnly
150-
{
151-
get { return _super.IsReadOnly; }
152-
}
77+
var main = new SubDictionary<string, object>(dict, s => !s.StartsWith("__with"));
78+
if (!load.TryGetValue(main, out withContainers))
79+
{
80+
withContainers = new Dictionary<string, WithContainer>();
81+
load.Add(main, withContainers);
82+
foreach (var tuple in ExtractSingleObjects(dict))
83+
{
84+
var withContainer = new WithContainer();
85+
withContainer.SetSingle(tuple.Item2);
86+
withContainers.Add(tuple.Item1, withContainer);
87+
}
88+
}
15389

154-
public bool ContainsKey(TKey key)
155-
{
156-
return _keyFilter(key) && _super.ContainsKey(key);
90+
foreach (var tuple in ExtractCollectionObjects(dict))
91+
{
92+
if (!withContainers.ContainsKey(tuple.Item1))
93+
{
94+
withContainers.Add(tuple.Item1, new WithContainer());
95+
}
96+
withContainers[tuple.Item1].AddToCollection(tuple.Item2);
97+
}
98+
}
99+
return load;
157100
}
158101

159-
public void Add(TKey key, TValue value)
102+
private class WithContainer
160103
{
161-
_super.Add(key, value);
162-
}
104+
public HashSet<IDictionary<string, object>> Collection { get; private set; }
163105

164-
public bool Remove(TKey key)
165-
{
166-
return _super.Remove(key);
167-
}
106+
public IDictionary<string, object> Single { get; private set; }
168107

169-
public bool TryGetValue(TKey key, out TValue value)
170-
{
171-
return _super.TryGetValue(key, out value);
172-
}
108+
public void AddToCollection(IDictionary<string,object> row)
109+
{
110+
if (Collection == null) Collection = new HashSet<IDictionary<string, object>>(new DictionaryEqualityComparer());
111+
Collection.Add(row);
112+
}
173113

174-
public TValue this[TKey key]
175-
{
176-
get { return _super[key]; }
177-
set { _super[key] = value; }
114+
public void SetSingle(IDictionary<string,object> row)
115+
{
116+
Single = row;
117+
}
178118
}
179119

180-
public ICollection<TKey> Keys
120+
private IEnumerable<Tuple<string,Dictionary<string,object>>> ExtractSingleObjects(IDictionary<string,object> source)
181121
{
182-
get { return _super.Keys.Where(_keyFilter).ToList().AsReadOnly(); }
183-
}
122+
var names =
123+
source.Keys.Where(k => k.StartsWith("__with1__")).Select(
124+
k => k.Split(new[] {"__"}, StringSplitOptions.RemoveEmptyEntries)[1]).Distinct().ToList();
184125

185-
public ICollection<TValue> Values
186-
{
187-
get { return Filter().Select(kvp => kvp.Value).ToList().AsReadOnly(); }
126+
return from name in names
127+
let pattern = "__with1__" + name + "__"
128+
select Tuple.Create(name, source.Where(kvp => kvp.Key.StartsWith(pattern))
129+
.ToDictionary(kvp => kvp.Key.Replace(pattern, ""), kvp => kvp.Value, HomogenizedEqualityComparer.DefaultInstance));
188130
}
189-
190-
public SubDictionary(IDictionary<TKey, TValue> super, Func<TKey,bool> keyFilter)
131+
private IEnumerable<Tuple<string,Dictionary<string,object>>> ExtractCollectionObjects(IDictionary<string,object> source)
191132
{
192-
_super = super;
193-
_keyFilter = keyFilter;
194-
}
133+
var names =
134+
source.Keys.Where(k => k.StartsWith("__withn__")).Select(
135+
k => k.Split(new[] {"__"}, StringSplitOptions.RemoveEmptyEntries)[1]).Distinct().ToList();
195136

196-
IEnumerator IEnumerable.GetEnumerator()
197-
{
198-
return GetEnumerator();
137+
return from name in names
138+
let pattern = "__withn__" + name + "__"
139+
select Tuple.Create(name, source.Where(kvp => kvp.Key.StartsWith(pattern))
140+
.ToDictionary(kvp => kvp.Key.Replace(pattern, ""), kvp => kvp.Value, HomogenizedEqualityComparer.DefaultInstance));
199141
}
200142
}
201143
}

Simple.Data.Ado/QueryBuilder.cs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ private void SetQueryContext(SimpleQuery query)
6767
_columns = _table.Columns.Select(c => ObjectReference.FromStrings(_table.ActualName, c.ActualName)).ToArray();
6868
}
6969

70+
HandleWithClauses();
71+
72+
_whereCriteria = _query.Clauses.OfType<WhereClause>().Aggregate(SimpleExpression.Empty,
73+
(seed, where) => seed && where.Criteria);
74+
_havingCriteria = _query.Clauses.OfType<HavingClause>().Aggregate(SimpleExpression.Empty,
75+
(seed, having) => seed && having.Criteria);
76+
77+
_commandBuilder.SetText(GetSelectClause(_tableName));
78+
}
79+
80+
private void HandleWithClauses()
81+
{
7082
var withClauses = _query.Clauses.OfType<WithClause>().ToList();
7183
if (withClauses.Count > 0)
7284
{
@@ -78,20 +90,24 @@ private void SetQueryContext(SimpleQuery query)
7890
_columns.Concat(
7991
_schema.FindTable(withClause.ObjectReference.GetName()).Columns.Select(
8092
c => new ObjectReference(c.ActualName, withClause.ObjectReference)))
81-
.ToArray();
93+
.ToArray();
8294
}
8395
}
8496
_columns =
85-
_columns.OfType<ObjectReference>().Select(
86-
c => _schema.FindTable(c.GetOwner().GetName()) == _table ? c : c.As(string.Format("__with__{0}__{1}", c.GetOwner().GetAliasOrName(), c.GetName()))).ToArray();
97+
_columns.OfType<ObjectReference>()
98+
.Select(c => _schema.FindTable(c.GetOwner().GetName()) == _table ? c : AddWithAlias(c))
99+
.ToArray();
87100
}
101+
}
88102

89-
_whereCriteria = _query.Clauses.OfType<WhereClause>().Aggregate(SimpleExpression.Empty,
90-
(seed, where) => seed && where.Criteria);
91-
_havingCriteria = _query.Clauses.OfType<HavingClause>().Aggregate(SimpleExpression.Empty,
92-
(seed, having) => seed && having.Criteria);
93-
94-
_commandBuilder.SetText(GetSelectClause(_tableName));
103+
private ObjectReference AddWithAlias(ObjectReference c)
104+
{
105+
var relationType = _schema.GetRelationType(c.GetOwner().GetOwner().GetName(), c.GetOwner().GetName());
106+
if (relationType == RelationType.None) throw new InvalidOperationException("No Join found");
107+
return c.As(string.Format("__with{0}__{1}__{2}",
108+
relationType == RelationType.OneToMany
109+
? "n"
110+
: "1", c.GetOwner().GetAliasOrName(), c.GetName()));
95111
}
96112

97113
private void HandleJoins()

Simple.Data.Ado/Schema/DatabaseSchema.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,21 @@ public ObjectName BuildObjectName(String text)
115115
if (schemaDotTable.Length != 2) throw new InvalidOperationException("Could not parse table name.");
116116
return new ObjectName(schemaDotTable[0], schemaDotTable[1]);
117117
}
118+
119+
public RelationType GetRelationType(string fromTableName, string toTableName)
120+
{
121+
var fromTable = FindTable(fromTableName);
122+
123+
if (fromTable.GetMaster(toTableName) != null) return RelationType.ManyToOne;
124+
if (fromTable.GetDetail(toTableName) != null) return RelationType.OneToMany;
125+
return RelationType.None;
126+
}
127+
}
128+
129+
public enum RelationType
130+
{
131+
None,
132+
OneToMany,
133+
ManyToOne
118134
}
119135
}

Simple.Data.Ado/Simple.Data.Ado.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
<Compile Include="Schema\TableJoin.cs" />
136136
<Compile Include="Schema\TableType.cs" />
137137
<Compile Include="ObjectName.cs" />
138+
<Compile Include="SubDictionary.cs" />
138139
<Compile Include="TraceHelper.cs" />
139140
<Compile Include="TupleExtensions.cs" />
140141
<Compile Include="TypeHelper.cs" />

0 commit comments

Comments
 (0)