Skip to content

Dapper: micro-optimizations #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/Benchmarks/FortunesDapperMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,13 @@ public async Task Invoke(HttpContext httpContext, HtmlEncoder htmlEncoder)

private static async Task<IEnumerable<Fortune>> LoadRows(string connectionString, DbProviderFactory dbProviderFactory)
{
var result = new List<Fortune>();
List<Fortune> result;

using (var db = dbProviderFactory.CreateConnection())
{
db.ConnectionString = connectionString;
await db.OpenAsync();

result.AddRange(await db.QueryAsync<Fortune>("SELECT [Id], [Message] FROM [Fortune]"));
// note: don't need to open connection if only doing one thing; let dapper do it
result = (await db.QueryAsync<Fortune>("SELECT [Id], [Message] FROM [Fortune]")).AsList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming Dapper doesn't do any pre-sizing of the list, yes? I ask because it's a requirement of the test that it doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you mean "to match the expected data": then no, dapper doesn't do that, because it is consuming a raw IDataReader (DbDataReader on core-clr) API, and doesn't know the number of rows until it reaches the end. In this setup, it is essentially:

List<T> buffer = new List<T>();
while (await reader.ReadAsync(cancel).ConfigureAwait(false)) {
    buffer.Add(func(reader));
}
while (await reader.NextResultAsync(cancel).ConfigureAwait(false)) { }
return buffer;

(where func is a delegate that processes each row into the target data type)

Reference source is here; note that command.Buffered is true by default (most people screw up with things like concurrent commands, if handed back an open reader - so this defaults to the safest option of consuming the data before handing it back to the caller)

I ask because it's a requirement of the test that it doesn't.

Do you have a link to the test requirements? I would be happy to check them all.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @benaadams ; I have reviewed the requirements; dapper does nothing that would violate Test 4 rule 5, if that is the concern.

}

result.Add(new Fortune { Message = "Additional fortune added at request time." });
Expand Down
4 changes: 1 addition & 3 deletions src/Benchmarks/MultipleQueriesDapperMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,9 @@ private static async Task<World[]> LoadRows(int count, string connectionString,

for (int i = 0; i < count; i++)
{
var world = await db.QueryAsync<World>(
result[i] = await db.QueryFirstOrDefaultAsync<World>(
"SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id",
new { Id = _random.Next(1, 10001) });

result[i] = world.First();
}

db.Close();
Expand Down
9 changes: 2 additions & 7 deletions src/Benchmarks/SingleQueryDapperMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,10 @@ private static async Task<World> LoadRow(string connectionString, DbProviderFact
using (var db = dbProviderFactory.CreateConnection())
{
db.ConnectionString = connectionString;
await db.OpenAsync();

var world = await db.QueryAsync<World>(
// note: don't need to open connection if only doing one thing; let dapper do it
return await db.QueryFirstOrDefaultAsync<World>(
"SELECT [Id], [RandomNumber] FROM [World] WHERE [Id] = @Id",
new { Id = _random.Next(1, 10001) });

db.Close();

return world.First();
}
}
}
Expand Down