Skip to content

Commit 9ff621c

Browse files
committed
Redesigned the BatchingCommandSet for better performance
1 parent 48e7c01 commit 9ff621c

File tree

2 files changed

+83
-55
lines changed

2 files changed

+83
-55
lines changed

src/NHibernate/AdoNet/GenericBatchingBatcher.cs

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
24
using System.Data.Common;
35
using System.Text;
46
using NHibernate.AdoNet.Util;
5-
using NHibernate.Dialect;
67
using NHibernate.Exceptions;
78
using NHibernate.SqlCommand;
89

@@ -139,92 +140,88 @@ protected override void Dispose(bool isDisposing)
139140

140141
private partial class BatchingCommandSet : IDisposable
141142
{
142-
private DbCommand _batchCommand;
143143
private readonly GenericBatchingBatcher _batcher;
144+
private readonly SqlStringBuilder _sql = new SqlStringBuilder();
145+
private readonly List<SqlTypes.SqlType> _sqlTypes = new List<SqlTypes.SqlType>();
146+
private readonly List<BatchParameter> _parameters = new List<BatchParameter>();
147+
private CommandType _commandType;
148+
149+
private class BatchParameter
150+
{
151+
public ParameterDirection Direction { get; set; }
152+
153+
public byte Precision { get; set; }
154+
155+
public byte Scale { get; set; }
156+
157+
public int Size { get; set; }
158+
159+
public object Value { get; set; }
160+
}
144161

145162
public BatchingCommandSet(GenericBatchingBatcher batcher)
146163
{
147164
_batcher = batcher;
148165
}
149-
166+
150167
public int CountOfCommands { get; private set; }
151168

152169
public int CountOfParameters { get; private set; }
153170

154171
public void Append(DbParameterCollection parameters)
155172
{
156-
if (_batchCommand == null)
173+
if (CountOfCommands > 0)
157174
{
158-
_batchCommand = _batcher.Driver.GenerateCommand(
159-
_batcher.CurrentCommand.CommandType,
160-
_batcher.CurrentCommandSql,
161-
_batcher.CurrentCommandParameterTypes);
162-
UpdateCommandParameters(_batchCommand, parameters);
163-
CountOfParameters = parameters.Count;
175+
_sql.Add(_batcher.StatementTerminator.ToString());
164176
}
165177
else
166178
{
167-
// We need to create a new command with different parameter names to avoid duplicates
168-
var command = _batcher.Driver.GenerateCommand(
169-
_batcher.CurrentCommand.CommandType,
170-
PrepareSqlString(_batcher.CurrentCommandSql),
171-
_batcher.CurrentCommandParameterTypes);
172-
UpdateCommandParameters(command, parameters);
173-
_batchCommand.CommandText += $"{_batcher.StatementTerminator}{command.CommandText}";
174-
while (command.Parameters.Count > 0)
179+
_commandType = _batcher.CurrentCommand.CommandType;
180+
}
181+
_sql.Add(PrepareSqlString(_batcher.CurrentCommandSql));
182+
_sqlTypes.AddRange(_batcher.CurrentCommandParameterTypes);
183+
184+
foreach (DbParameter parameter in parameters)
185+
{
186+
_parameters.Add(new BatchParameter
175187
{
176-
var pram = command.Parameters[0];
177-
command.Parameters.RemoveAt(0);
178-
_batchCommand.Parameters.Add(pram);
179-
}
180-
command.Dispose();
188+
Direction = parameter.Direction,
189+
Precision = parameter.Precision,
190+
Scale = parameter.Scale,
191+
Size = parameter.Size,
192+
Value = parameter.Value
193+
});
181194
}
182195
CountOfCommands++;
183196
}
184197

185198
public int ExecuteNonQuery()
186199
{
187-
if (_batchCommand == null)
200+
if (CountOfCommands == 0)
188201
{
189202
return 0;
190203
}
191-
_batcher.Prepare(_batchCommand);
192-
return _batchCommand.ExecuteNonQuery();
193-
}
194-
195-
public void Clear()
196-
{
197-
_batchCommand?.Dispose();
198-
_batchCommand = null;
199-
CountOfParameters = 0;
200-
CountOfCommands = 0;
201-
}
202-
203-
public void Dispose()
204-
{
205-
Clear();
206-
}
207-
208-
private void UpdateCommandParameters(DbCommand command, DbParameterCollection parameters)
209-
{
210-
for (var i = 0; i < parameters.Count; i++)
204+
var batcherCommand = _batcher.Driver.GenerateCommand(
205+
_commandType,
206+
_sql.ToSqlString(),
207+
_sqlTypes.ToArray()
208+
);
209+
for (var i = 0; i < _parameters.Count; i++)
211210
{
212-
var parameter = parameters[i];
213-
var cmdParam = command.Parameters[i];
211+
var parameter = _parameters[i];
212+
var cmdParam = batcherCommand.Parameters[i];
214213
cmdParam.Value = parameter.Value;
215214
cmdParam.Direction = parameter.Direction;
216215
cmdParam.Precision = parameter.Precision;
217216
cmdParam.Scale = parameter.Scale;
218217
cmdParam.Size = parameter.Size;
219218
}
219+
_batcher.Prepare(batcherCommand);
220+
return batcherCommand.ExecuteNonQuery();
220221
}
221222

222223
private SqlString PrepareSqlString(SqlString sql)
223224
{
224-
if (_batchCommand == null)
225-
{
226-
return sql;
227-
}
228225
sql = sql.Copy();
229226
foreach (var part in sql)
230227
{
@@ -235,6 +232,20 @@ private SqlString PrepareSqlString(SqlString sql)
235232
}
236233
return sql;
237234
}
235+
236+
public void Clear()
237+
{
238+
CountOfParameters = 0;
239+
CountOfCommands = 0;
240+
_sql.Clear();
241+
_sqlTypes.Clear();
242+
_parameters.Clear();
243+
}
244+
245+
public void Dispose()
246+
{
247+
Clear();
248+
}
238249
}
239250
}
240251
}

src/NHibernate/Async/AdoNet/GenericBatchingBatcher.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99

1010

1111
using System;
12+
using System.Collections.Generic;
13+
using System.Data;
1214
using System.Data.Common;
1315
using System.Text;
1416
using NHibernate.AdoNet.Util;
15-
using NHibernate.Dialect;
1617
using NHibernate.Exceptions;
1718
using NHibernate.SqlCommand;
1819

@@ -27,7 +28,8 @@ public override async Task AddToBatchAsync(IExpectation expectation, Cancellatio
2728
{
2829
cancellationToken.ThrowIfCancellationRequested();
2930
var batchUpdate = CurrentCommand;
30-
if (_currentBatch.CountOfParameters + CurrentCommand.Parameters.Count > _maxNumberOfParameters)
31+
if (_maxNumberOfParameters.HasValue &&
32+
_currentBatch.CountOfParameters + CurrentCommand.Parameters.Count > _maxNumberOfParameters)
3133
{
3234
await (ExecuteBatchWithTimingAsync(batchUpdate, cancellationToken)).ConfigureAwait(false);
3335
}
@@ -99,12 +101,27 @@ private partial class BatchingCommandSet : IDisposable
99101
public async Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)
100102
{
101103
cancellationToken.ThrowIfCancellationRequested();
102-
if (_batchCommand == null)
104+
if (CountOfCommands == 0)
103105
{
104106
return 0;
105107
}
106-
await (_batcher.PrepareAsync(_batchCommand, cancellationToken)).ConfigureAwait(false);
107-
return await (_batchCommand.ExecuteNonQueryAsync(cancellationToken)).ConfigureAwait(false);
108+
var batcherCommand = _batcher.Driver.GenerateCommand(
109+
_commandType,
110+
_sql.ToSqlString(),
111+
_sqlTypes.ToArray()
112+
);
113+
for (var i = 0; i < _parameters.Count; i++)
114+
{
115+
var parameter = _parameters[i];
116+
var cmdParam = batcherCommand.Parameters[i];
117+
cmdParam.Value = parameter.Value;
118+
cmdParam.Direction = parameter.Direction;
119+
cmdParam.Precision = parameter.Precision;
120+
cmdParam.Scale = parameter.Scale;
121+
cmdParam.Size = parameter.Size;
122+
}
123+
await (_batcher.PrepareAsync(batcherCommand, cancellationToken)).ConfigureAwait(false);
124+
return await (batcherCommand.ExecuteNonQueryAsync(cancellationToken)).ConfigureAwait(false);
108125
}
109126
}
110127
}

0 commit comments

Comments
 (0)