Skip to content

Commit 14cd9d4

Browse files
committed
Converting works with object graphs
1 parent d24c1e4 commit 14cd9d4

11 files changed

Lines changed: 310 additions & 15 deletions

File tree

Simple.Data.Ado/QueryBuilder.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,14 @@ private void SetQueryContext(SimpleQuery query)
6060
var selectClause = _query.Clauses.OfType<SelectClause>().SingleOrDefault();
6161
if (selectClause != null)
6262
{
63-
_columns = selectClause.Columns.ToArray();
63+
if (selectClause.Columns.OfType<AllColumnsSpecialReference>().Any())
64+
{
65+
_columns = ExpandAllColumnsReferences(selectClause.Columns).ToArray();
66+
}
67+
else
68+
{
69+
_columns = selectClause.Columns.ToArray();
70+
}
6471
}
6572
else
6673
{
@@ -77,6 +84,22 @@ private void SetQueryContext(SimpleQuery query)
7784
_commandBuilder.SetText(GetSelectClause(_tableName));
7885
}
7986

87+
private IEnumerable<SimpleReference> ExpandAllColumnsReferences(IEnumerable<SimpleReference> columns)
88+
{
89+
foreach (var column in columns)
90+
{
91+
var allColumns = column as AllColumnsSpecialReference;
92+
if (ReferenceEquals(allColumns, null)) yield return column;
93+
else
94+
{
95+
foreach (var allColumn in _schema.FindTable(allColumns.Table.GetName()).Columns)
96+
{
97+
yield return new ObjectReference(allColumn.ActualName, allColumns.Table);
98+
}
99+
}
100+
}
101+
}
102+
80103
private void HandleWithClauses()
81104
{
82105
var withClauses = _query.Clauses.OfType<WithClause>().ToList();

Simple.Data.Ado/SimpleReferenceFormatter.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,25 @@ private string FormatColumnClause(SimpleReference reference, bool excludeAlias)
3333
??
3434
TryFormatAsFunctionReference(reference as FunctionReference, excludeAlias)
3535
??
36-
TryFormatAsMathReference(reference as MathReference, excludeAlias);
36+
TryFormatAsMathReference(reference as MathReference, excludeAlias)
37+
??
38+
TryFormatAsAllColumnsReference(reference as AllColumnsSpecialReference, excludeAlias);
3739

3840
if (formatted != null) return formatted;
3941

4042
throw new InvalidOperationException("SimpleReference type not supported.");
4143
}
4244

45+
private string TryFormatAsAllColumnsReference(AllColumnsSpecialReference allColumnsSpecialReference, bool excludeAlias)
46+
{
47+
if (ReferenceEquals(allColumnsSpecialReference, null)) return null;
48+
var table = _schema.FindTable(allColumnsSpecialReference.Table.GetAllObjectNamesDotted());
49+
var tableName = string.IsNullOrWhiteSpace(allColumnsSpecialReference.GetAlias())
50+
? table.QualifiedName
51+
: _schema.QuoteObjectName(allColumnsSpecialReference.GetAlias());
52+
return string.Format("{0}.*", tableName);
53+
}
54+
4355
private string FormatObject(object obj)
4456
{
4557
var reference = obj as SimpleReference;

Simple.Data.BehaviourTest/Query/ExplicitJoinTest.cs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,46 @@ public void JoinWithExplicitClauseUsingNamedParameters()
5656
" join [dbo].[department] on ([dbo].[department].[id] = [dbo].[employee].[departmentid])");
5757
}
5858

59+
[Test]
60+
public void OuterJoinWithExplicitClauseUsingNamedParameters()
61+
{
62+
var q = _db.Employees.Query()
63+
.OuterJoin(_db.Department, Id: _db.Employees.DepartmentId)
64+
.Select(_db.Employees.Name, _db.Department.Name.As("Department"));
65+
66+
try
67+
{
68+
q.ToList();
69+
}
70+
catch (InvalidOperationException)
71+
{
72+
// This won't work on Mock provider, but the SQL should be generated OK
73+
}
74+
75+
GeneratedSqlIs("select [dbo].[employee].[name],[dbo].[department].[name] as [Department] from [dbo].[employee]" +
76+
" left join [dbo].[department] on ([dbo].[department].[id] = [dbo].[employee].[departmentid])");
77+
}
78+
79+
[Test]
80+
public void LeftJoinWithExplicitClauseUsingNamedParameters()
81+
{
82+
var q = _db.Employees.Query()
83+
.LeftJoin(_db.Department, Id: _db.Employees.DepartmentId)
84+
.Select(_db.Employees.Name, _db.Department.Name.As("Department"));
85+
86+
try
87+
{
88+
q.ToList();
89+
}
90+
catch (InvalidOperationException)
91+
{
92+
// This won't work on Mock provider, but the SQL should be generated OK
93+
}
94+
95+
GeneratedSqlIs("select [dbo].[employee].[name],[dbo].[department].[name] as [Department] from [dbo].[employee]" +
96+
" left join [dbo].[department] on ([dbo].[department].[id] = [dbo].[employee].[departmentid])");
97+
}
98+
5999
[Test]
60100
public void JoinWithExplicitClauseUsingExpression()
61101
{
@@ -76,6 +116,26 @@ public void JoinWithExplicitClauseUsingExpression()
76116
" join [dbo].[department] on ([dbo].[department].[id] = [dbo].[employee].[departmentid])");
77117
}
78118

119+
[Test]
120+
public void LeftJoinWithExplicitClauseUsingExpression()
121+
{
122+
var q = _db.Employees.Query()
123+
.LeftJoin(_db.Department).On(_db.Department.Id == _db.Employees.DepartmentId)
124+
.Select(_db.Employees.Name, _db.Department.Name.As("Department"));
125+
126+
try
127+
{
128+
q.ToList();
129+
}
130+
catch (InvalidOperationException)
131+
{
132+
// This won't work on Mock provider, but the SQL should be generated OK
133+
}
134+
135+
GeneratedSqlIs("select [dbo].[employee].[name],[dbo].[department].[name] as [Department] from [dbo].[employee]" +
136+
" left join [dbo].[department] on ([dbo].[department].[id] = [dbo].[employee].[departmentid])");
137+
}
138+
79139
[Test]
80140
public void SelfJoinWithExplicitClauseUsingNamedParameters()
81141
{
@@ -97,6 +157,27 @@ public void SelfJoinWithExplicitClauseUsingNamedParameters()
97157
" join [dbo].[employee] [manager] on ([manager].[id] = [dbo].[employee].[managerid])");
98158
}
99159

160+
[Test]
161+
public void LeftSelfJoinWithExplicitClauseUsingNamedParameters()
162+
{
163+
var q = _db.Employees.Query()
164+
.LeftJoin(_db.Employees.As("Manager"), Id: _db.Employees.ManagerId);
165+
166+
q = q.Select(_db.Employees.Name, q.Manager.Name.As("Manager"));
167+
168+
try
169+
{
170+
q.ToList();
171+
}
172+
catch (InvalidOperationException)
173+
{
174+
// This won't work on Mock provider, but the SQL should be generated OK
175+
}
176+
177+
GeneratedSqlIs("select [dbo].[employee].[name],[manager].[name] as [Manager] from [dbo].[employee]" +
178+
" left join [dbo].[employee] [manager] on ([manager].[id] = [dbo].[employee].[managerid])");
179+
}
180+
100181
[Test]
101182
public void SelfJoinWithExplicitClauseUsingOutParameterAndNamedParameters()
102183
{
@@ -118,6 +199,27 @@ public void SelfJoinWithExplicitClauseUsingOutParameterAndNamedParameters()
118199
" join [dbo].[employee] [manager] on ([manager].[id] = [dbo].[employee].[managerid])");
119200
}
120201

202+
[Test]
203+
public void LeftSelfJoinWithExplicitClauseUsingOutParameterAndNamedParameters()
204+
{
205+
dynamic manager;
206+
var q = _db.Employees.Query()
207+
.LeftJoin(_db.Employees.As("Manager"), out manager).On(Id: _db.Employees.ManagerId)
208+
.Select(_db.Employees.Name, manager.Name.As("Manager"));
209+
210+
try
211+
{
212+
q.ToList();
213+
}
214+
catch (InvalidOperationException)
215+
{
216+
// This won't work on Mock provider, but the SQL should be generated OK
217+
}
218+
219+
GeneratedSqlIs("select [dbo].[employee].[name],[manager].[name] as [Manager] from [dbo].[employee]" +
220+
" left join [dbo].[employee] [manager] on ([manager].[id] = [dbo].[employee].[managerid])");
221+
}
222+
121223
[Test]
122224
public void SelfJoinWithExplicitClauseUsingExpression()
123225
{

Simple.Data.BehaviourTest/Query/QueryTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ public void SpecifyingColumnsShouldRestrictSelect()
4343
GeneratedSqlIs("select [dbo].[users].[name],[dbo].[users].[password] from [dbo].[users]");
4444
}
4545

46+
[Test]
47+
public void SpecifyingColumnStarShouldSelectAllColumns()
48+
{
49+
_db.Users.All()
50+
.Select(_db.Users.Name, _db.Users.UserBio.Star())
51+
.ToList();
52+
GeneratedSqlIs("select [dbo].[users].[name],[dbo].[userbio].[userid],[dbo].[userbio].[text] from [dbo].[users]" +
53+
" left join [dbo].[userbio] on ([dbo].[users].[id] = [dbo].[userbio].[userid])");
54+
}
55+
56+
[Test]
57+
public void SpecifyingColumnAllColumnsShouldSelectAllColumns()
58+
{
59+
_db.Users.All()
60+
.Select(_db.Users.Name, _db.Users.UserBio.AllColumns())
61+
.ToList();
62+
GeneratedSqlIs("select [dbo].[users].[name],[dbo].[userbio].[userid],[dbo].[userbio].[text] from [dbo].[users]" +
63+
" left join [dbo].[userbio] on ([dbo].[users].[id] = [dbo].[userbio].[userid])");
64+
}
65+
4666
[Test]
4767
public void SpecifyingColumnWithAliasShouldAddAsClause()
4868
{

Simple.Data.BehaviourTest/Query/WithTest.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,46 @@ public void SingleWithClauseUsingExplicitJoinShouldApplyAliasToSql()
115115

116116
GeneratedSqlIs(expectedSql);
117117
}
118+
119+
[Test]
120+
public void SingleWithOneClauseUsingExplicitJoinShouldApplyAliasToSql()
121+
{
122+
const string expectedSql = "select [dbo].[employee].[id],[dbo].[employee].[name]," +
123+
"[dbo].[employee].[managerid],[dbo].[employee].[departmentid]," +
124+
"[manager].[id] as [__with1__manager__id],[manager].[name] as [__with1__manager__name]," +
125+
"[manager].[managerid] as [__with1__manager__managerid],[manager].[departmentid] as [__with1__manager__departmentid]" +
126+
" from [dbo].[employee] left join [dbo].[employee] [manager] on ([manager].[id] = [dbo].[employee].[managerid])";
127+
128+
dynamic manager;
129+
var q = _db.Employees.All()
130+
.OuterJoin(_db.Employees.As("Manager"), out manager).On(Id: _db.Employees.ManagerId)
131+
.WithOne(manager);
132+
133+
EatException(() => q.ToList());
134+
135+
GeneratedSqlIs(expectedSql);
136+
}
137+
138+
[Test]
139+
public void MultipleWithClauseJustDoesEverythingYouWouldHope()
140+
{
141+
const string expectedSql = "select [dbo].[employee].[id],[dbo].[employee].[name]," +
142+
"[dbo].[employee].[managerid],[dbo].[employee].[departmentid]," +
143+
"[manager].[id] as [__withn__manager__id],[manager].[name] as [__withn__manager__name]," +
144+
"[manager].[managerid] as [__withn__manager__managerid],[manager].[departmentid] as [__withn__manager__departmentid]," +
145+
"[dbo].[department].[id] as [__with1__department__id],[dbo].[department].[name] as [__with1__department__name]" +
146+
" from [dbo].[employee] left join [dbo].[employee] [manager] on ([manager].[id] = [dbo].[employee].[managerid])" +
147+
" left join [dbo].[department] on ([dbo].[department].[id] = [dbo].[employee].[departmentid])";
148+
149+
dynamic manager;
150+
var q = _db.Employees.All()
151+
.OuterJoin(_db.Employees.As("Manager"), out manager).On(Id: _db.Employees.ManagerId)
152+
.With(manager)
153+
.WithDepartment();
154+
155+
EatException(() => q.ToList());
156+
157+
GeneratedSqlIs(expectedSql);
158+
}
118159
}
119160
}

Simple.Data.UnitTest/ConcreteTypeCreatorTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void CanConvertStringToEnum()
5757
Assert.IsInstanceOf<Int32ToEnum>(actual);
5858
Assert.AreEqual(expected, ((Int32ToEnum)actual).Value);
5959
}
60-
60+
6161
public class DecimalToDouble
6262
{
6363
public double Value { get; set; }

Simple.Data.UnitTest/Simple.Data.UnitTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
<Compile Include="SimpleQueryTest.cs" />
103103
<Compile Include="SimpleRecordAsDictionaryTest.cs" />
104104
<Compile Include="SimpleRecordCloneTest.cs" />
105+
<Compile Include="SimpleRecordConvertTest.cs" />
105106
<Compile Include="SimpleResultSetTest.cs" />
106107
<Compile Include="SpecialReferenceTests.cs" />
107108
<Compile Include="StringExtensionsTest.cs" />
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace Simple.Data.UnitTest
2+
{
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using NUnit.Framework;
6+
7+
[TestFixture]
8+
public class SimpleRecordConvertTest
9+
{
10+
[Test]
11+
public void CanConvertToFoo()
12+
{
13+
dynamic source = new SimpleRecord(new Dictionary<string, object> {{"X", "Bar"}});
14+
Foo actual = source;
15+
Assert.AreEqual("Bar", actual.X);
16+
}
17+
18+
[Test]
19+
public void CanConvertWithSubItemToFoo()
20+
{
21+
dynamic source = new SimpleRecord(new Dictionary<string, object> {{"X", "Bar"}, {"Y", new Dictionary<string,object> { {"X", "Quux"}}}});
22+
Foo actual = source;
23+
Assert.AreEqual("Bar", actual.X);
24+
Assert.IsNotNull(actual.Y);
25+
Assert.AreEqual("Quux", actual.Y.X);
26+
}
27+
28+
[Test]
29+
public void CanConvertWithSubItemAndCollectionToFoo()
30+
{
31+
dynamic source =
32+
new SimpleRecord(new Dictionary<string, object>
33+
{{"X", "Bar"},
34+
{"Y", new Dictionary<string, object> {{"X", "Quux"}}},
35+
{"Z", new[] { new Dictionary<string, object> {{"X", "Wibble"}}}}
36+
});
37+
Foo actual = source;
38+
Assert.AreEqual("Bar", actual.X);
39+
Assert.IsNotNull(actual.Y);
40+
Assert.AreEqual("Quux", actual.Y.X);
41+
Assert.IsNotNull(actual.Z);
42+
Assert.AreEqual(1, actual.Z.Count);
43+
Assert.AreEqual("Wibble", actual.Z.Single().X);
44+
}
45+
46+
public class Foo
47+
{
48+
public string X { get; set; }
49+
public Foo Y { get; set; }
50+
public ICollection<Foo> Z { get; set; }
51+
}
52+
}
53+
}

Simple.Data/AllColumnsSpecialReference.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,20 @@ namespace Simple.Data
22
{
33
public class AllColumnsSpecialReference : SpecialReference
44
{
5-
public AllColumnsSpecialReference() : base("*")
5+
private readonly ObjectReference _table;
6+
7+
public AllColumnsSpecialReference() : this(null)
68
{
79
}
10+
11+
public AllColumnsSpecialReference(ObjectReference table) : base("*")
12+
{
13+
_table = table;
14+
}
15+
16+
public ObjectReference Table
17+
{
18+
get { return _table; }
19+
}
820
}
921
}

Simple.Data/ObjectReference.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ public override string GetAliasOrName()
7878
return GetAlias() ?? _name;
7979
}
8080

81+
public AllColumnsSpecialReference AllColumns()
82+
{
83+
return new AllColumnsSpecialReference(this);
84+
}
85+
86+
public AllColumnsSpecialReference Star()
87+
{
88+
return new AllColumnsSpecialReference(this);
89+
}
90+
8191
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
8292
{
8393
if (_dataStrategy != null)

0 commit comments

Comments
 (0)