Skip to content

Commit 2af683b

Browse files
committed
Added WithTotalCount and a bunch of stuff to support bulk ops
1 parent bbeb9c7 commit 2af683b

23 files changed

+447
-19
lines changed

Simple.Data.Ado/AdoAdapter.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,56 @@ public override IEnumerable<IDictionary<string, object>> RunQuery(SimpleQuery qu
9999
.ToEnumerable(connection);
100100
}
101101

102+
private IEnumerable<IDictionary<string, object>> RunQueryWithCount(SimpleQuery query, WithCountClause withCountClause, out IEnumerable<SimpleQueryClauseBase> unhandledClauses)
103+
{
104+
var countQuery = query.ClearSkip().ClearTake().Select(new CountSpecialReference());
105+
var unhandledClausesList = new List<IEnumerable<SimpleQueryClauseBase>>(2);
106+
using (var enumerator = RunQueries(new[] { countQuery, query }, unhandledClausesList).GetEnumerator())
107+
{
108+
unhandledClauses = unhandledClausesList[1];
109+
if (!enumerator.MoveNext())
110+
{
111+
throw new InvalidOperationException();
112+
}
113+
var countRow = enumerator.Current.Single();
114+
withCountClause.SetCount((int) countRow.First().Value);
115+
if (!enumerator.MoveNext())
116+
{
117+
throw new InvalidOperationException();
118+
}
119+
return enumerator.Current;
120+
}
121+
}
122+
123+
public override IEnumerable<IEnumerable<IDictionary<string,object>>> RunQueries(SimpleQuery[] queries, List<IEnumerable<SimpleQueryClauseBase>> unhandledClauses)
124+
{
125+
if (ProviderSupportsCompoundStatements && queries.Length > 1)
126+
{
127+
var commandBuilders = new ICommandBuilder[queries.Length];
128+
for (int i = 0; i < queries.Length; i++)
129+
{
130+
IEnumerable<SimpleQueryClauseBase> unhandledClausesForThisQuery;
131+
commandBuilders[i] = new QueryBuilder(this, i).Build(queries[i], out unhandledClausesForThisQuery);
132+
unhandledClauses.Add(unhandledClausesForThisQuery);
133+
}
134+
var connection = _connectionProvider.CreateConnection();
135+
var command = CommandBuilder.CreateCommand(commandBuilders, connection);
136+
foreach (var item in command.ToEnumerables(connection))
137+
{
138+
yield return item.ToList();
139+
}
140+
}
141+
else
142+
{
143+
foreach (SimpleQuery t in queries)
144+
{
145+
IEnumerable<SimpleQueryClauseBase> unhandledClausesForThisQuery;
146+
yield return RunQuery(t, out unhandledClausesForThisQuery);
147+
unhandledClauses.Add(unhandledClausesForThisQuery);
148+
}
149+
}
150+
}
151+
102152
public override IObservable<IDictionary<string, object>> RunQueryAsObservable(SimpleQuery query, out IEnumerable<SimpleQueryClauseBase> unhandledClauses)
103153
{
104154
var connection = _connectionProvider.CreateConnection();

Simple.Data.Ado/CommandBuilder.cs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class CommandBuilder : ICommandBuilder
1515
private readonly ISchemaProvider _schemaProvider;
1616
private readonly Dictionary<ParameterTemplate, object> _parameters = new Dictionary<ParameterTemplate, object>();
1717
private readonly StringBuilder _text;
18+
private readonly string _parameterSuffix;
1819

1920
public string Joins { get; set; }
2021

@@ -24,23 +25,24 @@ public CommandBuilder(ISchemaProvider schemaProvider)
2425
_schemaProvider = schemaProvider;
2526
}
2627

27-
public CommandBuilder(string text, ISchemaProvider schemaProvider)
28+
public CommandBuilder(string text, ISchemaProvider schemaProvider, int bulkIndex)
2829
{
2930
_text = new StringBuilder(text);
3031
_schemaProvider = schemaProvider;
32+
_parameterSuffix = (bulkIndex >= 0) ? "_c" + bulkIndex : string.Empty;
3133
}
3234

3335
public ParameterTemplate AddParameter(object value, Column column)
3436
{
35-
string name = _schemaProvider.NameParameter("p" + Interlocked.Increment(ref _number));
37+
string name = _schemaProvider.NameParameter("p" + Interlocked.Increment(ref _number) + _parameterSuffix);
3638
var parameterTemplate = new ParameterTemplate(name, column);
3739
_parameters.Add(parameterTemplate, value);
3840
return parameterTemplate;
3941
}
4042

4143
public ParameterTemplate AddParameter(string name, DbType dbType, object value)
4244
{
43-
name = _schemaProvider.NameParameter(name);
45+
name = _schemaProvider.NameParameter(name + _parameterSuffix);
4446
var parameterTemplate = new ParameterTemplate(name, dbType, 0);
4547
_parameters.Add(parameterTemplate, value);
4648
return parameterTemplate;
@@ -76,7 +78,7 @@ public IDbCommand GetCommand(IDbConnection connection)
7678
{
7779
var command = connection.CreateCommand();
7880
command.CommandText = Text;
79-
SetParameters(command);
81+
SetParameters(command, string.Empty);
8082
return command;
8183
}
8284

@@ -103,11 +105,17 @@ public CommandTemplate GetCommandTemplate(Table table)
103105
return new CommandTemplate(_text.ToString(), _parameters.Keys.ToArray(), new Dictionary<string, int>(index, HomogenizedEqualityComparer.DefaultInstance));
104106
}
105107

106-
private void SetParameters(IDbCommand command)
108+
private void SetParameters(IDbCommand command, string suffix)
107109
{
108-
if (_parameters.Any(kvp => kvp.Value is IRange) || _parameters.Any(kvp => kvp.Value is IEnumerable && !(kvp.Value is string)))
110+
SetParameters(command, _parameters);
111+
}
112+
113+
private static void SetParameters(IDbCommand command, IEnumerable<KeyValuePair<ParameterTemplate, object>> parameters)
114+
{
115+
var parameterList = parameters.ToList();
116+
if (parameterList.Any(kvp => kvp.Value is IRange) || parameterList.Any(kvp => kvp.Value is IEnumerable && !(kvp.Value is string)))
109117
{
110-
foreach (var pair in _parameters)
118+
foreach (var pair in parameterList)
111119
{
112120
foreach (var parameter in CreateParameterComplex(pair.Key, pair.Value, command))
113121
{
@@ -117,7 +125,7 @@ private void SetParameters(IDbCommand command)
117125
}
118126
else
119127
{
120-
foreach (var pair in _parameters)
128+
foreach (var pair in parameterList)
121129
{
122130
command.Parameters.Add(CreateSingleParameter(pair.Value, command, pair.Key.Name, pair.Key.DbType));
123131
}
@@ -205,5 +213,17 @@ private static IDbDataParameter CreateSingleParameter(object value, IDbCommand c
205213
parameter.Value = CommandHelper.FixObjectType(value);
206214
return parameter;
207215
}
216+
217+
internal static IDbCommand CreateCommand(ICommandBuilder[] commandBuilders, IDbConnection connection)
218+
{
219+
var command = connection.CreateCommand();
220+
for (int i = 0; i < commandBuilders.Length; i++)
221+
{
222+
if (!string.IsNullOrWhiteSpace(command.CommandText)) command.CommandText += "; ";
223+
command.CommandText += commandBuilders[i].Text;
224+
SetParameters(command, commandBuilders[i].Parameters);
225+
}
226+
return command;
227+
}
208228
}
209229
}

Simple.Data.Ado/DataReaderEnumerator.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,20 @@ namespace Simple.Data.Ado
99
using System.Data;
1010
using System.Data.Common;
1111

12-
class DataReaderEnumerator : IEnumerator<IDictionary<string,object>>
12+
internal class DataReaderEnumerator : IEnumerator<IDictionary<string, object>>
1313
{
1414
private readonly IDbConnection _connection;
1515
private IDictionary<string, int> _index;
1616
private readonly IDbCommand _command;
1717
private IDataReader _reader;
1818
private bool _lastRead;
1919

20-
public DataReaderEnumerator(IDbCommand command, IDbConnection connection) : this(command, connection, null)
20+
public DataReaderEnumerator(IDbCommand command, IDbConnection connection)
21+
: this(command, connection, null)
2122
{
2223
}
2324

24-
public DataReaderEnumerator(IDbCommand command, IDbConnection connection, IDictionary<string,int> index)
25+
public DataReaderEnumerator(IDbCommand command, IDbConnection connection, IDictionary<string, int> index)
2526
{
2627
_command = command;
2728
_connection = connection;
@@ -33,7 +34,9 @@ public void Dispose()
3334
using (_connection)
3435
using (_command)
3536
using (_reader)
36-
{ /* NO-OP */ }
37+
{
38+
/* NO-OP */
39+
}
3740
}
3841

3942
public bool MoveNext()
@@ -80,4 +83,4 @@ object IEnumerator.Current
8083
get { return Current; }
8184
}
8285
}
83-
}
86+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
namespace Simple.Data.Ado
2+
{
3+
using System;
4+
using System.Collections;
5+
using System.Collections.Generic;
6+
using System.Data;
7+
using System.Data.Common;
8+
9+
class DataReaderMultipleEnumerator : IEnumerator<IEnumerable<IDictionary<string, object>>>
10+
{
11+
private readonly IDbConnection _connection;
12+
private IDictionary<string, int> _index;
13+
private readonly IDbCommand _command;
14+
private IDataReader _reader;
15+
private bool _lastRead;
16+
17+
public DataReaderMultipleEnumerator(IDbCommand command, IDbConnection connection) : this(command, connection, null)
18+
{
19+
}
20+
21+
public DataReaderMultipleEnumerator(IDbCommand command, IDbConnection connection, IDictionary<string, int> index)
22+
{
23+
_command = command;
24+
_connection = connection;
25+
_index = index;
26+
}
27+
28+
public void Dispose()
29+
{
30+
using (_connection)
31+
using (_command)
32+
using (_reader)
33+
{
34+
/* NO-OP */
35+
}
36+
}
37+
38+
public bool MoveNext()
39+
{
40+
if (_reader == null)
41+
{
42+
ExecuteReader();
43+
_lastRead = true;
44+
return true;
45+
}
46+
_lastRead = _reader.NextResult();
47+
return _lastRead;
48+
}
49+
50+
private void ExecuteReader()
51+
{
52+
try
53+
{
54+
if (_connection.State == ConnectionState.Closed)
55+
_connection.Open();
56+
_reader = _command.ExecuteReader();
57+
_index = _index ?? _reader.CreateDictionaryIndex();
58+
}
59+
catch (DbException ex)
60+
{
61+
throw new AdoAdapterException(ex.Message, ex);
62+
}
63+
}
64+
65+
public void Reset()
66+
{
67+
if (_reader != null) _reader.Dispose();
68+
ExecuteReader();
69+
}
70+
71+
public IEnumerable<IDictionary<string, object>> Current
72+
{
73+
get
74+
{
75+
if (!_lastRead) throw new InvalidOperationException();
76+
var index = _reader.CreateDictionaryIndex();
77+
while (_reader.Read())
78+
{
79+
yield return _reader.ToDictionary(index);
80+
}
81+
}
82+
}
83+
84+
object IEnumerator.Current
85+
{
86+
get { return Current; }
87+
}
88+
}
89+
}

Simple.Data.Ado/DbCommandExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public static IEnumerable<IDictionary<string, object>> ToEnumerable(this IDbComm
1414
return ToEnumerable(command, connection, null);
1515
}
1616

17+
public static IEnumerable<IEnumerable<IDictionary<string, object>>> ToEnumerables(this IDbCommand command, IDbConnection connection)
18+
{
19+
return new DataReaderMultipleEnumerator(command, connection).Wrap();
20+
}
21+
1722
public static IEnumerable<IDictionary<string, object>> ToEnumerable(this IDbCommand command, IDbConnection connection, IDictionary<string, int> index)
1823
{
1924
return new DataReaderEnumerator(command, connection, index).Wrap();

Simple.Data.Ado/ICommandBuilder.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ public interface ICommandBuilder
1313
IDbCommand GetRepeatableCommand(IDbConnection connection);
1414
CommandTemplate GetCommandTemplate(Table table);
1515
IEnumerable<KeyValuePair<ParameterTemplate, object>> Parameters { get; }
16+
string Text { get; }
1617
}
1718
}

Simple.Data.Ado/ParameterTemplate.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ public Column Column
5050
get { return _column; }
5151
}
5252

53+
public ParameterTemplate Rename(string newName)
54+
{
55+
return _column == null
56+
? new ParameterTemplate(newName, _dbType, _maxLength)
57+
: new ParameterTemplate(newName, _column);
58+
}
59+
5360
public bool Equals(ParameterTemplate other)
5461
{
5562
if (ReferenceEquals(null, other)) return false;

Simple.Data.Ado/QueryBuilder.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class QueryBuilder
1111
private readonly IFunctionNameConverter _functionNameConverter = new FunctionNameConverter();
1212
private readonly SimpleReferenceFormatter _simpleReferenceFormatter;
1313
private readonly AdoAdapter _adoAdapter;
14+
private readonly int _bulkIndex;
1415
private readonly DatabaseSchema _schema;
1516

1617
private ObjectName _tableName;
@@ -20,11 +21,16 @@ public class QueryBuilder
2021
private SimpleExpression _havingCriteria;
2122
private SimpleReference[] _columns;
2223
private CommandBuilder _commandBuilder;
23-
private List<SimpleQueryClauseBase> _unhandledClauses;
24+
private List<SimpleQueryClauseBase> _unhandledClauses;
2425

25-
public QueryBuilder(AdoAdapter adoAdapter)
26+
public QueryBuilder(AdoAdapter adoAdapter) : this(adoAdapter, -1)
27+
{
28+
}
29+
30+
public QueryBuilder(AdoAdapter adoAdapter, int bulkIndex)
2631
{
2732
_adoAdapter = adoAdapter;
33+
_bulkIndex = bulkIndex;
2834
_schema = _adoAdapter.GetSchema();
2935
_simpleReferenceFormatter = new SimpleReferenceFormatter(_schema);
3036
}
@@ -65,7 +71,7 @@ private void SetQueryContext(SimpleQuery query)
6571

6672
_tableName = ObjectName.Parse(query.TableName.Split('.').Last());
6773
_table = _schema.FindTable(_tableName);
68-
_commandBuilder = new CommandBuilder(GetSelectClause(_tableName), _schema.SchemaProvider);
74+
_commandBuilder = new CommandBuilder(GetSelectClause(_tableName), _schema.SchemaProvider, _bulkIndex);
6975
}
7076

7177
private void HandleJoins()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<Compile Include="DataParameterCollectionEx.cs" />
6464
<Compile Include="DataReaderEnumerator.cs" />
6565
<Compile Include="DataReaderExtensions.cs" />
66+
<Compile Include="DataReaderMultipleEnumerator.cs" />
6667
<Compile Include="DataRecordExtensions.cs" />
6768
<Compile Include="DbCommandExtensions.cs" />
6869
<Compile Include="DbConnectionExtensions.cs" />
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
namespace Simple.Data.IntegrationTest.Query
7+
{
8+
using Mocking.Ado;
9+
using NUnit.Framework;
10+
11+
[TestFixture]
12+
public class WithCountTest : DatabaseIntegrationContext
13+
{
14+
protected override void SetSchema(MockSchemaProvider schemaProvider)
15+
{
16+
schemaProvider.SetTables(new[] { "dbo", "Users", "BASE TABLE" });
17+
18+
schemaProvider.SetColumns(new[] { "dbo", "Users", "Name" },
19+
new[] { "dbo", "Users", "Password" });
20+
}
21+
22+
[Test]
23+
public void WithTotalCountShouldCreateCompoundQuery()
24+
{
25+
const string expected = @"select count(*) from [dbo].[users] where [dbo].[users].[name] = @p1_c0; " +
26+
@"select [dbo].[users].[name],[dbo].[users].[password] from [dbo].[users] where [dbo].[users].[name] = @p1_c1";
27+
28+
Future<int> count;
29+
var q = _db.Users.QueryByName("Foo")
30+
.WithTotalCount(out count);
31+
32+
EatException<InvalidOperationException>(() => q.ToList());
33+
34+
GeneratedSqlIs(expected);
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)