Skip to content

Commit 1215bf1

Browse files
authored
Merge pull request #94 from DataObjects-NET/metadata-extensions-memory-traffic
Fix high memory traffic while extracting metadata
2 parents d402836 + 94d7145 commit 1215bf1

File tree

4 files changed

+82
-45
lines changed

4 files changed

+82
-45
lines changed

Orm/Xtensive.Orm/Orm/Providers/Interfaces/ISqlExecutor.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created: 2012.02.29
66

77
using System.Collections.Generic;
8+
using System.Data;
89
using System.Data.Common;
910
using System.Threading;
1011
using System.Threading.Tasks;
@@ -21,8 +22,9 @@ public interface ISqlExecutor
2122
/// Executes the specified query statement. This method is similar to <see cref="DbCommand.ExecuteReader()"/>.
2223
/// </summary>
2324
/// <param name="statement">The statement to execute.</param>
25+
/// <param name="commandBehavior">Options for statement execution and data retrieval.</param>
2426
/// <returns>Result of execution.</returns>
25-
CommandWithDataReader ExecuteReader(ISqlCompileUnit statement);
27+
CommandWithDataReader ExecuteReader(ISqlCompileUnit statement, CommandBehavior commandBehavior = CommandBehavior.Default);
2628

2729
/// <summary>
2830
/// Asynchronously executes the specified query statement.
@@ -34,13 +36,27 @@ public interface ISqlExecutor
3436
/// <param name="token">The cancellation token to terminate execution if needed.</param>
3537
/// <returns>Result of execution.</returns>
3638
Task<CommandWithDataReader> ExecuteReaderAsync(ISqlCompileUnit statement, CancellationToken token = default);
39+
40+
/// <summary>
41+
/// Asynchronously executes the specified query statement.
42+
/// This method is similar to <see cref="DbCommand.ExecuteReaderAsync()"/>.
43+
/// </summary>
44+
/// <remarks> Multiple active operations are not supported. Use <see langword="await"/>
45+
/// to ensure that all asynchronous operations have completed.</remarks>
46+
/// <param name="statement">The statement to execute.</param>
47+
/// <param name="commandBehavior">Options for statement execution and data retrieval.</param>
48+
/// <param name="token">The cancellation token to terminate execution if needed.</param>
49+
/// <returns>Result of execution.</returns>
50+
Task<CommandWithDataReader> ExecuteReaderAsync(
51+
ISqlCompileUnit statement, CommandBehavior commandBehavior, CancellationToken token = default);
3752

3853
/// <summary>
3954
/// Executes the specified query statement. This method is similar to <see cref="DbCommand.ExecuteReader()"/>.
4055
/// </summary>
4156
/// <param name="commandText">The statement to execute.</param>
57+
/// <param name="commandBehavior">Options for statement execution and data retrieval.</param>
4258
/// <returns>Result of execution.</returns>
43-
CommandWithDataReader ExecuteReader(string commandText);
59+
CommandWithDataReader ExecuteReader(string commandText, CommandBehavior commandBehavior = CommandBehavior.Default);
4460

4561
/// <summary>
4662
/// Asynchronously executes the specified query statement.
@@ -51,6 +67,17 @@ public interface ISqlExecutor
5167
/// <returns>Result of execution.</returns>
5268
Task<CommandWithDataReader> ExecuteReaderAsync(string commandText, CancellationToken token = default);
5369

70+
/// <summary>
71+
/// Asynchronously executes the specified query statement.
72+
/// This method is similar to <see cref="DbCommand.ExecuteReaderAsync()"/>.
73+
/// </summary>
74+
/// <param name="commandText">The statement to execute.</param>
75+
/// <param name="commandBehavior">Options for statement execution and data retrieval.</param>
76+
/// <param name="token">The cancellation token to terminate execution if needed.</param>
77+
/// <returns>Result of execution.</returns>
78+
Task<CommandWithDataReader> ExecuteReaderAsync(
79+
string commandText, CommandBehavior commandBehavior, CancellationToken token = default);
80+
5481
/// <summary>
5582
/// Executes the specified scalar statement. This method is similar to <see cref="DbCommand.ExecuteScalar"/>.
5683
/// </summary>

Orm/Xtensive.Orm/Orm/Providers/SqlExecutor.cs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Created: 2012.02.29
66

77
using System.Collections.Generic;
8+
using System.Data;
89
using System.Data.Common;
910
using System.Linq;
1011
using System.Threading;
@@ -23,17 +24,23 @@ internal sealed class SqlExecutor : ISqlExecutor
2324
private readonly StorageDriver driver;
2425
private readonly Session session;
2526

26-
public CommandWithDataReader ExecuteReader(ISqlCompileUnit statement)
27+
public CommandWithDataReader ExecuteReader(
28+
ISqlCompileUnit statement, CommandBehavior commandBehavior = CommandBehavior.Default)
2729
{
2830
EnsureConnectionIsOpen();
29-
return ExecuteReader(connection.CreateCommand(Compile(statement)));
31+
return ExecuteReader(connection.CreateCommand(Compile(statement)), commandBehavior);
3032
}
3133

34+
public Task<CommandWithDataReader> ExecuteReaderAsync(
35+
ISqlCompileUnit statement, CancellationToken token = default) =>
36+
ExecuteReaderAsync(statement, CommandBehavior.Default, token);
37+
3238
public async Task<CommandWithDataReader> ExecuteReaderAsync(
33-
ISqlCompileUnit statement, CancellationToken token = default)
39+
ISqlCompileUnit statement, CommandBehavior commandBehavior, CancellationToken token = default)
3440
{
3541
await EnsureConnectionIsOpenAsync(token).ConfigureAwait(false);
36-
return await ExecuteReaderAsync(connection.CreateCommand(Compile(statement)), token).ConfigureAwait(false);
42+
return await ExecuteReaderAsync(
43+
connection.CreateCommand(Compile(statement)), commandBehavior, token).ConfigureAwait(false);
3744
}
3845

3946
public int ExecuteNonQuery(ISqlCompileUnit statement)
@@ -68,16 +75,22 @@ public async Task<object> ExecuteScalarAsync(ISqlCompileUnit statement, Cancella
6875
}
6976
}
7077

71-
public CommandWithDataReader ExecuteReader(string commandText)
78+
public CommandWithDataReader ExecuteReader(
79+
string commandText, CommandBehavior commandBehavior = CommandBehavior.Default)
7280
{
7381
EnsureConnectionIsOpen();
74-
return ExecuteReader(connection.CreateCommand(commandText));
82+
return ExecuteReader(connection.CreateCommand(commandText), commandBehavior);
7583
}
7684

77-
public async Task<CommandWithDataReader> ExecuteReaderAsync(string commandText, CancellationToken token = default)
85+
public Task<CommandWithDataReader> ExecuteReaderAsync(string commandText, CancellationToken token = default) =>
86+
ExecuteReaderAsync(commandText, CommandBehavior.Default, token);
87+
88+
public async Task<CommandWithDataReader> ExecuteReaderAsync(
89+
string commandText, CommandBehavior commandBehavior, CancellationToken token = default)
7890
{
7991
await EnsureConnectionIsOpenAsync(token).ConfigureAwait(false);
80-
return await ExecuteReaderAsync(connection.CreateCommand(commandText), token).ConfigureAwait(false);
92+
return await ExecuteReaderAsync(
93+
connection.CreateCommand(commandText), commandBehavior, token).ConfigureAwait(false);
8194
}
8295

8396
public int ExecuteNonQuery(string commandText)
@@ -242,11 +255,11 @@ private string Compile(ISqlCompileUnit statement)
242255
return driver.Compile(statement, session.StorageNode.Configuration).GetCommandText();
243256
}
244257

245-
private CommandWithDataReader ExecuteReader(DbCommand command)
258+
private CommandWithDataReader ExecuteReader(DbCommand command, CommandBehavior commandBehavior)
246259
{
247260
DbDataReader reader;
248261
try {
249-
reader = driver.ExecuteReader(session, command);
262+
reader = driver.ExecuteReader(session, command, commandBehavior);
250263
}
251264
catch {
252265
command.Dispose();
@@ -255,11 +268,11 @@ private CommandWithDataReader ExecuteReader(DbCommand command)
255268
return new CommandWithDataReader(command, reader);
256269
}
257270

258-
private async Task<CommandWithDataReader> ExecuteReaderAsync(DbCommand command, CancellationToken token)
271+
private async Task<CommandWithDataReader> ExecuteReaderAsync(DbCommand command, CommandBehavior commandBehavior, CancellationToken token)
259272
{
260273
DbDataReader reader;
261274
try {
262-
reader = await driver.ExecuteReaderAsync(session, command, token).ConfigureAwait(false);
275+
reader = await driver.ExecuteReaderAsync(session, command, commandBehavior, token).ConfigureAwait(false);
263276
}
264277
catch {
265278
await command.DisposeAsync().ConfigureAwait(false);

Orm/Xtensive.Orm/Orm/Providers/StorageDriver.Operations.cs

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -392,45 +392,40 @@ public async ValueTask ReleaseSavepointAsync(
392392
#region Sync Execute methods
393393

394394
public int ExecuteNonQuery(Session session, DbCommand command) =>
395-
ExecuteCommand(session, command, c => c.ExecuteNonQuery());
395+
ExecuteCommand(session, command, CommandBehavior.Default, (c, cb) => c.ExecuteNonQuery());
396396

397397
public object ExecuteScalar(Session session, DbCommand command) =>
398-
ExecuteCommand(session, command, c => c.ExecuteScalar());
398+
ExecuteCommand(session, command, CommandBehavior.Default, (c, cb) => c.ExecuteScalar());
399399

400-
public DbDataReader ExecuteReader(Session session, DbCommand command) =>
401-
ExecuteCommand(session, command, c => c.ExecuteReader());
400+
public DbDataReader ExecuteReader(Session session, DbCommand command,
401+
CommandBehavior behavior = CommandBehavior.Default) =>
402+
ExecuteCommand(session, command, behavior, (c, cb) => c.ExecuteReader(cb));
402403

403404
#endregion
404405

405406
#region Async Execute methods
406407

407-
public Task<int> ExecuteNonQueryAsync(Session session, DbCommand command) =>
408-
ExecuteCommandAsync(session, command, CancellationToken.None,
409-
(c, ct) => c.ExecuteNonQueryAsync(ct));
408+
public Task<int> ExecuteNonQueryAsync(Session session, DbCommand command, CancellationToken cancellationToken = default) =>
409+
ExecuteCommandAsync(session, command, CommandBehavior.Default, cancellationToken,
410+
(c, cb, ct) => c.ExecuteNonQueryAsync(ct));
410411

411-
public Task<int> ExecuteNonQueryAsync(Session session, DbCommand command, CancellationToken cancellationToken) =>
412-
ExecuteCommandAsync(session, command, cancellationToken,
413-
(c, ct) => c.ExecuteNonQueryAsync(ct));
412+
public Task<object> ExecuteScalarAsync(Session session, DbCommand command, CancellationToken cancellationToken = default) =>
413+
ExecuteCommandAsync(session, command, CommandBehavior.Default, cancellationToken,
414+
(c, cb, ct) => c.ExecuteScalarAsync(ct));
414415

415-
public Task<object> ExecuteScalarAsync(Session session, DbCommand command) =>
416-
ExecuteCommandAsync(session, command, CancellationToken.None,
417-
(c, ct) => c.ExecuteScalarAsync(ct));
416+
public Task<DbDataReader> ExecuteReaderAsync(Session session, DbCommand command,
417+
CancellationToken cancellationToken = default) =>
418+
ExecuteReaderAsync(session, command, CommandBehavior.Default, cancellationToken);
418419

419-
public Task<object> ExecuteScalarAsync(Session session, DbCommand command, CancellationToken cancellationToken) =>
420-
ExecuteCommandAsync(session, command, cancellationToken,
421-
(c, ct) => c.ExecuteScalarAsync(ct));
422-
423-
public Task<DbDataReader> ExecuteReaderAsync(Session session, DbCommand command) =>
424-
ExecuteCommandAsync(session, command, CancellationToken.None,
425-
(c, ct) => c.ExecuteReaderAsync(ct));
426-
427-
public Task<DbDataReader> ExecuteReaderAsync(Session session, DbCommand command, CancellationToken cancellationToken) =>
428-
ExecuteCommandAsync(session, command, cancellationToken,
429-
(c, ct) => c.ExecuteReaderAsync(ct));
420+
public Task<DbDataReader> ExecuteReaderAsync(
421+
Session session, DbCommand command, CommandBehavior behavior, CancellationToken cancellationToken = default) =>
422+
ExecuteCommandAsync(session, command, behavior, cancellationToken,
423+
(c, cb, ct) => c.ExecuteReaderAsync(cb, ct));
430424

431425
#endregion
432426

433-
private TResult ExecuteCommand<TResult>(Session session, DbCommand command, Func<DbCommand, TResult> action)
427+
private TResult ExecuteCommand<TResult>(
428+
Session session, DbCommand command, CommandBehavior commandBehavior, Func<DbCommand, CommandBehavior, TResult> action)
434429
{
435430
if (isLoggingEnabled) {
436431
SqlLog.Info(Strings.LogSessionXQueryY, session.ToStringSafely(), command.ToHumanReadableString());
@@ -440,7 +435,7 @@ private TResult ExecuteCommand<TResult>(Session session, DbCommand command, Func
440435

441436
TResult result;
442437
try {
443-
result = action.Invoke(command);
438+
result = action.Invoke(command, commandBehavior);
444439
}
445440
catch (Exception exception) {
446441
var wrapped = ExceptionBuilder.BuildException(exception, command.ToHumanReadableString());
@@ -453,8 +448,9 @@ private TResult ExecuteCommand<TResult>(Session session, DbCommand command, Func
453448
return result;
454449
}
455450

456-
private async Task<TResult> ExecuteCommandAsync<TResult>(Session session, DbCommand command,
457-
CancellationToken cancellationToken, Func<DbCommand, CancellationToken, Task<TResult>> action)
451+
private async Task<TResult> ExecuteCommandAsync<TResult>(Session session,
452+
DbCommand command, CommandBehavior commandBehavior,
453+
CancellationToken cancellationToken, Func<DbCommand, CommandBehavior, CancellationToken, Task<TResult>> action)
458454
{
459455
if (isLoggingEnabled) {
460456
SqlLog.Info(Strings.LogSessionXQueryY, session.ToStringSafely(), command.ToHumanReadableString());
@@ -465,7 +461,7 @@ private async Task<TResult> ExecuteCommandAsync<TResult>(Session session, DbComm
465461

466462
TResult result;
467463
try {
468-
result = await action(command, cancellationToken).ConfigureAwait(false);
464+
result = await action(command, commandBehavior, cancellationToken).ConfigureAwait(false);
469465
}
470466
catch (OperationCanceledException) {
471467
throw;

Orm/Xtensive.Orm/Orm/Upgrade/Internals/Metadata/MetadataExtractor.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
using System;
88
using System.Collections.Generic;
9+
using System.Data;
910
using System.Data.Common;
1011
using System.Linq;
1112
using System.Threading;
@@ -130,7 +131,7 @@ private TypeMetadata ParseType(DbDataReader reader)
130131

131132
private void ExecuteQuery<T>(ICollection<T> output, ISqlCompileUnit query, Func<DbDataReader, T> parser)
132133
{
133-
using var command = executor.ExecuteReader(query);
134+
using var command = executor.ExecuteReader(query, CommandBehavior.SequentialAccess);
134135
var reader = command.Reader;
135136
while (reader.Read()) {
136137
output.Add(parser.Invoke(reader));
@@ -140,7 +141,7 @@ private void ExecuteQuery<T>(ICollection<T> output, ISqlCompileUnit query, Func<
140141
private async Task ExecuteQueryAsync<T>(ICollection<T> output, ISqlCompileUnit query, Func<DbDataReader, T> parser,
141142
CancellationToken token)
142143
{
143-
var command = await executor.ExecuteReaderAsync(query, token).ConfigureAwait(false);
144+
var command = await executor.ExecuteReaderAsync(query, CommandBehavior.SequentialAccess, token).ConfigureAwait(false);
144145
await using (command.ConfigureAwait(false)) {
145146
var reader = command.Reader;
146147
while (await reader.ReadAsync(token).ConfigureAwait(false)) {

0 commit comments

Comments
 (0)