diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1913e336..c005d433 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Dapper.AOT/CommandFactory.cs b/src/Dapper.AOT/CommandFactory.cs
index 435c0e39..c2c7a1e9 100644
--- a/src/Dapper.AOT/CommandFactory.cs
+++ b/src/Dapper.AOT/CommandFactory.cs
@@ -159,6 +159,26 @@ protected static bool TryRecycle(ref DbCommand? storage, DbCommand command)
command.Transaction = null;
return Interlocked.CompareExchange(ref storage, command, null) is null;
}
+
+
+#if NET6_0_OR_GREATER
+ ///
+ /// Provides an opportunity to recycle and reuse batch instances
+ ///
+ protected static bool TryRecycle(ref DbBatch? storage, DbBatch batch)
+ {
+ // detach and recycle
+ batch.Connection = null;
+ batch.Transaction = null;
+ return Interlocked.CompareExchange(ref storage, batch, null) is null;
+ }
+
+ ///
+ /// Provides an opportunity to recycle and reuse batch instances
+ ///
+ public virtual bool TryRecycle(DbBatch batch) => false;
+#endif
+
}
///
@@ -190,8 +210,7 @@ public virtual DbCommand GetCommand(DbConnection connection, string sql, Command
internal void Initialize(in UnifiedCommand cmd,
string sql, CommandType commandType, T args)
{
- cmd.CommandText = sql;
- cmd.CommandType = commandType != 0 ? commandType : DapperAotExtensions.GetCommandType(sql);
+ cmd.SetCommand(sql, commandType != 0 ? commandType : DapperAotExtensions.GetCommandType(sql));
AddParameters(in cmd, args);
}
@@ -216,9 +235,10 @@ public virtual void AddParameters(in UnifiedCommand command, T args)
///
public virtual void UpdateParameters(in UnifiedCommand command, T args)
{
- if (command.Parameters.Count != 0) // try to avoid rogue "dirty" checks
+ var ps = command.Parameters;
+ if (ps.Count != 0) // try to avoid rogue "dirty" checks
{
- command.Parameters.Clear();
+ ps.Clear();
}
AddParameters(in command, args);
}
@@ -234,10 +254,9 @@ public virtual void UpdateParameters(in UnifiedCommand command, T args)
// try to avoid any dirty detection in the setters
if (cmd.CommandText != sql) cmd.CommandText = sql;
if (cmd.CommandType != commandType) cmd.CommandType = commandType;
- UpdateParameters(new(cmd), args);
+ UpdateParameters(new UnifiedCommand(cmd), args);
}
return cmd;
-
}
///
@@ -257,10 +276,52 @@ public virtual void UpdateParameters(in UnifiedCommand command, T args)
#pragma warning restore IDE0079 // following will look unnecessary on up-level
public virtual bool UseBatch(string sql) => false;
+#if NET6_0_OR_GREATER
+ ///
+ /// Create a populated batch from a command
+ ///
+ public virtual DbBatch GetBatch(DbConnection connection, string sql, CommandType commandType, T args)
+ {
+ Debug.Assert(connection.CanCreateBatch);
+ var batch = connection.CreateBatch();
+ batch.Connection = connection;
+ AddCommands(new(batch), sql, args);
+ return batch;
+ }
+
+ ///
+ /// Provides an opportunity to recycle and reuse batch instances
+ ///
+ protected DbBatch? TryReuse(ref DbBatch? storage, T args)
+ {
+ var batch = Interlocked.Exchange(ref storage, null);
+ if (batch is not null)
+ {
+ // try to avoid any dirty detection in the setters
+ UpdateParameters(new UnifiedBatch(batch), args);
+ }
+ return batch;
+ }
+#endif
+
+
///
/// Allows the caller to rewrite a composite command into a multi-command batch.
///
- public virtual void AddCommands(in UnifiedBatch batch, string sql, T args) => throw new NotSupportedException();
+ public virtual void AddCommands(in UnifiedBatch batch, string sql, T args)
+ {
+ // implement as basic mode
+ batch.SetCommand(sql, CommandType.Text);
+ AddParameters(in batch.Command, args);
+ }
+
+ ///
+ /// Allows the caller to update the parameter values of a multi-command batch.
+ ///
+ public virtual void UpdateParameters(in UnifiedBatch batch, T args)
+ {
+ UpdateParameters(in batch.Command, args);
+ }
///
/// Allows an implementation to process output parameters etc after a multi-command batch has completed.
diff --git a/src/Dapper.AOT/CommandT.cs b/src/Dapper.AOT/CommandT.cs
index 5490f3df..6da8a203 100644
--- a/src/Dapper.AOT/CommandT.cs
+++ b/src/Dapper.AOT/CommandT.cs
@@ -90,12 +90,25 @@ private void GetUnifiedBatch(out UnifiedBatch batch, TArgs args) // it will most
{
if (commandType == CommandType.Text && commandFactory.UseBatch(sql))
{
- batch = new UnifiedBatch(connection, transaction);
- if (timeout >= 0)
+#if NET6_0_OR_GREATER
+ if (connection.CanCreateBatch)
{
- batch.TimeoutSeconds = timeout;
+ var dbBatch = commandFactory.GetBatch(connection, sql, commandType, args);
+ dbBatch.Connection = connection;
+ dbBatch.Timeout = timeout;
+ dbBatch.Transaction = transaction;
+ batch = new(dbBatch);
+ }
+ else
+#endif
+ {
+ var cmd = connection.CreateCommand();
+ cmd.Connection = connection;
+ cmd.Transaction = transaction;
+ cmd.CommandTimeout = timeout;
+ batch = new(cmd);
+ commandFactory.AddCommands(in batch, sql, args);
}
- commandFactory.AddCommands(in batch, sql, args);
}
else
{
@@ -118,11 +131,7 @@ internal void PostProcessAndRecycleUnified(ref SyncCommandState state, TArgs arg
{
Debug.Assert(state.Command is null); // all on unified command now
commandFactory.PostProcess(in state.UnifiedBatch.Command, args, rowCount);
-
- if (state.UnifiedBatch.Command.Command is { } cmd && commandFactory.TryRecycle(cmd))
- {
- state.UnifiedBatch.Command.ClearSource();
- }
+ state.UnifiedBatch.Command.TryRecycle(commandFactory);
}
internal void PostProcessAndRecycle(AsyncQueryState state, TArgs args, int rowCount)
diff --git a/src/Dapper.AOT/UnifiedBatch.cs b/src/Dapper.AOT/UnifiedBatch.cs
index 6a9be398..64737530 100644
--- a/src/Dapper.AOT/UnifiedBatch.cs
+++ b/src/Dapper.AOT/UnifiedBatch.cs
@@ -3,7 +3,6 @@
using System.Data;
using System.Data.Common;
using System.Diagnostics;
-using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
@@ -33,6 +32,15 @@ internal UnifiedBatch(DbCommand command)
Debug.Assert(Command.CommandCount == 1);
}
+#if NET6_0_OR_GREATER
+ internal UnifiedBatch(DbBatch batch)
+ {
+ Command = new UnifiedCommand(batch);
+ commandStart = 0;
+ commandCount = batch.BatchCommands.Count;
+ Debug.Assert(Command.CommandCount > 0); // could be multiple for batch re-use scenarios
+ }
+#endif
internal UnifiedBatch(DbConnection connection, DbTransaction? transaction)
{
@@ -91,13 +99,21 @@ private int GetCommandIndex(int localIndex)
public string CommandText
{
get => Command.CommandText;
+ [Obsolete("When possible, " + nameof(SetCommand) + " should be preferred", false)]
set => Command.CommandText = value;
}
+ ///
+ /// Initialize the and
+ ///
+ public void SetCommand(string commandText, CommandType commandType = CommandType.Text)
+ => Command.SetCommand(commandText, commandType);
+
///
public CommandType CommandType
{
get => Command.CommandType;
+ [Obsolete("When possible, " + nameof(SetCommand) + " should be preferred", false)]
set => Command.CommandType = value;
}
diff --git a/src/Dapper.AOT/UnifiedCommand.cs b/src/Dapper.AOT/UnifiedCommand.cs
index b6e0748f..c843a41a 100644
--- a/src/Dapper.AOT/UnifiedCommand.cs
+++ b/src/Dapper.AOT/UnifiedCommand.cs
@@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
-using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
@@ -104,6 +104,7 @@ public string CommandText
#endif
_ => "",
};
+ [Obsolete("When possible, " + nameof(SetCommand) + " should be preferred", false)]
set
{
switch (_source)
@@ -148,6 +149,7 @@ public CommandType CommandType
#endif
_ => CommandType.Text,
};
+ [Obsolete("When possible, " + nameof(SetCommand) + " should be preferred", false)]
set
{
switch (_source)
@@ -203,25 +205,31 @@ public int TimeoutSeconds
///
public DbParameter AddParameter()
{
- // TODO: optimize to avoid double tests
- var p = CreateParameter();
- Parameters.Add(p);
+ var p = CreateParameter(out var parameters);
+ parameters.Add(p);
return p;
}
///
- public DbParameter CreateParameter()
+ public DbParameter CreateParameter() => CreateParameter(out _);
+
+ ///
+ private DbParameter CreateParameter(out DbParameterCollection parameters)
{
switch (_source)
{
case DbCommand cmd:
+ parameters = cmd.Parameters;
return cmd.CreateParameter();
case List list:
- return list[_index].CreateParameter();
+ var activeCmd = list[_index];
+ parameters = activeCmd.Parameters;
+ return activeCmd.CreateParameter();
#if NET6_0_OR_GREATER
case DbBatch batch:
-#if NET8_0_OR_GREATER // https://github.com/dotnet/runtime/issues/82326
var bc = batch.BatchCommands[_index];
+ parameters = bc.Parameters;
+#if NET8_0_OR_GREATER // https://github.com/dotnet/runtime/issues/82326
if (bc.CanCreateParameter) return bc.CreateParameter();
#endif // NET8
return (_spareCommandForParameters ?? UnsafeBatchWithCommandForParameters()).CreateParameter();
@@ -246,12 +254,15 @@ internal UnifiedCommand(DbCommand command)
internal UnifiedCommand(DbBatch batch)
{
- // withCommand is typically true for a ready-to-go command; it is false if, for example, we're
- // doing a multi-row exec and want to start completely empty
_source = batch;
_spareCommandForParameters = null;
-
- batch.BatchCommands.Add(batch.CreateBatchCommand());
+
+ var bc = batch.BatchCommands;
+ if (bc.Count == 0)
+ {
+ // initialize the first command
+ bc.Add(batch.CreateBatchCommand());
+ }
_index = 0;
}
@@ -264,8 +275,6 @@ private DbCommand UnsafeBatchWithCommandForParameters()
}
#endif
- internal void ClearSource() => Unsafe.AsRef(in _source) = null!;
-
internal void PostProcess(IEnumerable source, CommandFactory commandFactory)
{
var snapshot = _index;
@@ -461,19 +470,15 @@ internal DbDataReader ExecuteReader(CommandBehavior flags)
internal Task ExecuteNonQueryAsync(CancellationToken cancellationToken)
{
- switch (_source)
+ return _source switch
{
- case DbCommand cmd:
- return cmd.ExecuteNonQueryAsync(cancellationToken);
- case List list:
- return ExecuteListAsync(list, cancellationToken);
+ DbCommand cmd => cmd.ExecuteNonQueryAsync(cancellationToken),
+ List list => ExecuteListAsync(list, cancellationToken),
#if NET6_0_OR_GREATER
- case DbBatch batch:
- return batch.ExecuteNonQueryAsync(cancellationToken);
+ DbBatch batch => batch.ExecuteNonQueryAsync(cancellationToken),
#endif
- default:
- return TaskZero;
- }
+ _ => TaskZero,
+ };
static async Task ExecuteListAsync(List list, CancellationToken cancellationToken)
{
@@ -486,5 +491,53 @@ static async Task ExecuteListAsync(List list, CancellationToken
}
}
+ ///
+ /// Initialize the and
+ ///
+ public void SetCommand(string commandText, CommandType commandType = CommandType.Text)
+ {
+ switch (_source)
+ {
+ // note we're trying to avoid triggering any unnecessary side-effects and
+ // cache-invalidations that could be triggered from setters
+ case DbCommand cmd:
+ if (cmd.CommandText != commandText) cmd.CommandText = commandText;
+ if (cmd.CommandType != commandType) cmd.CommandType = commandType;
+ break;
+ case List list:
+ var activeCmd = list[_index];
+ if (activeCmd.CommandText != commandText) activeCmd.CommandText = commandText;
+ if (activeCmd.CommandType != commandType) activeCmd.CommandType = commandType;
+ break;
+#if NET6_0_OR_GREATER
+ case DbBatch batch:
+ var bc = batch.BatchCommands[_index];
+ if (bc.CommandText != commandText) bc.CommandText = commandText;
+ if (bc.CommandType != commandType) bc.CommandType = commandType;
+ break;
+#endif
+ }
+ }
+
+ internal void TryRecycle(CommandFactory commandFactory)
+ {
+ if (_source switch
+ {
+ // note we're trying to avoid triggering any unnecessary side-effects and
+ // cache-invalidations that could be triggered from setters
+ DbCommand cmd => commandFactory.TryRecycle(cmd),
+ // note we don't expect to recycle list usage in this way; we're only expecting
+ // single-arg scenarios
+#if NET6_0_OR_GREATER
+ DbBatch batch => commandFactory.TryRecycle(batch),
+#endif
+ _ => false,
+ })
+ {
+ // wipe the source - someone else can see it
+ Unsafe.AsRef(in _source) = null!;
+ }
+ }
+
private static readonly Task TaskZero = Task.FromResult(0);
}
\ No newline at end of file
diff --git a/test/UsageBenchmark/CommandRewriteBenchmarks.cs b/test/UsageBenchmark/CommandRewriteBenchmarks.cs
index 0ad86646..5cfc5975 100644
--- a/test/UsageBenchmark/CommandRewriteBenchmarks.cs
+++ b/test/UsageBenchmark/CommandRewriteBenchmarks.cs
@@ -47,7 +47,7 @@ public class MyArgsType
public string? Name3 { get; set; }
}
- public static readonly MyArgsType Args = new MyArgsType { Name0 = "abc", Name1 = "def", Name2 ="ghi", Name3 = "jkl" };
+ public static readonly MyArgsType Args = new() { Name0 = "abc", Name1 = "def", Name2 = "ghi", Name3 = "jkl" };
// for our test, we're going to do 4 operations and try rewriting it as batch
public const string BasicSql = """
@@ -61,53 +61,59 @@ public class MyArgsType
public int Dapper() => npgsql.Execute(BasicSql, Args);
// note these are hand written
+
[Benchmark]
- public int DapperAOT_Simple() => npgsql.Command(BasicSql, handler: BasicCommand.Instance).Execute(Args);
+ public int DapperAOT() => npgsql.Command(BasicSql, handler: BasicCommand.NonCached).Execute(Args);
[Benchmark]
- public int DapperAOT_Rewrite() => npgsql.Command(BasicSql, handler: RewriteCommand.Instance).Execute(Args);
+ public int DapperAOT_Cached() => npgsql.Command(BasicSql, handler: BasicCommand.Cached).Execute(Args);
- static DbCommand CreateCommand(DbConnection connection)
- {
- var cmd = connection.CreateCommand();
- cmd.Connection = connection;
- cmd.CommandText = BasicSql;
- cmd.CommandType = CommandType.Text;
+ [Benchmark]
+ public int DapperAOT_Batch() => npgsql.Command(BasicSql, handler: RewriteCommand.NonCached).Execute(Args);
- var ps = cmd.Parameters;
+ [Benchmark]
+ public int DapperAOT_BatchCached() => npgsql.Command(BasicSql, handler: RewriteCommand.Cached).Execute(Args);
- var p = cmd.CreateParameter();
- p.ParameterName = "Name0";
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name0;
- ps.Add(p);
-
- p = cmd.CreateParameter();
- p.ParameterName = "Name1";
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name1;
- ps.Add(p);
-
- p = cmd.CreateParameter();
- p.ParameterName = "Name2";
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name1;
- ps.Add(p);
-
- p = cmd.CreateParameter();
- p.ParameterName = "Name3";
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name1;
- ps.Add(p);
-
- return cmd;
- }
+ static NpgsqlCommand CreateCommand(NpgsqlConnection connection) => new()
+ {
+ Connection = connection,
+ CommandText = BasicSql,
+ CommandType = CommandType.Text,
- static void UpdateCommand(DbCommand cmd, DbConnection connection)
+ Parameters =
+ {
+ new NpgsqlParameter
+ {
+ ParameterName = "Name0",
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name0
+ },
+ new NpgsqlParameter
+ {
+ ParameterName = "Name1",
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name1
+ },
+ new NpgsqlParameter
+ {
+ ParameterName = "Name2",
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name2
+ },
+ new NpgsqlParameter
+ {
+ ParameterName = "Name3",
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name3
+ },
+ },
+ };
+
+ static void UpdateCommand(NpgsqlCommand cmd, NpgsqlConnection connection)
{
cmd.Connection = connection;
var ps = cmd.Parameters;
@@ -124,7 +130,7 @@ public int AdoNetCommand()
return cmd.ExecuteNonQuery();
}
- static DbCommand? _spareCommand;
+ static NpgsqlCommand? _spareCommand;
[Benchmark]
public int AdoNetCommandCached()
@@ -143,65 +149,81 @@ public int AdoNetCommandCached()
Interlocked.Exchange(ref _spareCommand, cmd)?.Dispose();
return result;
}
- private static DbBatch CreateBatch(DbConnection connection)
+ private static NpgsqlBatch CreateBatch(NpgsqlConnection connection) => new()
{
- var batch = connection.CreateBatch();
- batch.Connection = connection;
-
- var cmd = batch.CreateBatchCommand();
- cmd.CommandText = "insert into RewriteCustomers(Name) values($1)";
- cmd.CommandType = CommandType.Text;
- var p = new NpgsqlParameter();
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name0;
- cmd.Parameters.Add(p);
- batch.BatchCommands.Add(cmd);
-
- cmd = batch.CreateBatchCommand();
- cmd.CommandText = "insert into RewriteCustomers(Name) values($1)";
- cmd.CommandType = CommandType.Text;
- p = new NpgsqlParameter();
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name1;
- cmd.Parameters.Add(p);
- batch.BatchCommands.Add(cmd);
-
- cmd = batch.CreateBatchCommand();
- cmd.CommandText = "insert into RewriteCustomers(Name) values($1)";
- cmd.CommandType = CommandType.Text;
- p = new NpgsqlParameter();
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name2;
- cmd.Parameters.Add(p);
- batch.BatchCommands.Add(cmd);
-
- cmd = batch.CreateBatchCommand();
- cmd.CommandText = "insert into RewriteCustomers(Name) values($1)";
- cmd.CommandType = CommandType.Text;
- p = new NpgsqlParameter();
- p.DbType = DbType.String;
- p.Size = -1;
- p.Value = Args.Name3;
- cmd.Parameters.Add(p);
- batch.BatchCommands.Add(cmd);
-
- return batch;
- }
-
- static void UpdateBatch(DbBatch batch, DbConnection connection)
+ Connection = connection,
+ BatchCommands =
+ {
+ new NpgsqlBatchCommand
+ {
+ CommandText = "insert into RewriteCustomers(Name) values($1)",
+ CommandType = CommandType.Text,
+ Parameters =
+ {
+ new NpgsqlParameter
+ {
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name0,
+ }
+ },
+ },
+ new NpgsqlBatchCommand
+ {
+ CommandText = "insert into RewriteCustomers(Name) values($1)",
+ CommandType = CommandType.Text,
+ Parameters =
+ {
+ new NpgsqlParameter
+ {
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name1,
+ }
+ },
+ },
+ new NpgsqlBatchCommand
+ {
+ CommandText = "insert into RewriteCustomers(Name) values($1)",
+ CommandType = CommandType.Text,
+ Parameters =
+ {
+ new NpgsqlParameter
+ {
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name2,
+ }
+ },
+ },
+ new NpgsqlBatchCommand
+ {
+ CommandText = "insert into RewriteCustomers(Name) values($1)",
+ CommandType = CommandType.Text,
+ Parameters =
+ {
+ new NpgsqlParameter
+ {
+ DbType = DbType.String,
+ Size = -1,
+ Value = Args.Name3,
+ }
+ },
+ },
+ },
+ };
+
+ static void UpdateBatch(NpgsqlBatch batch, NpgsqlConnection connection)
{
batch.Connection = connection;
- var cmds = batch.BatchCommands;
- cmds[0].Parameters[0].Value = Args.Name0;
- cmds[1].Parameters[0].Value = Args.Name1;
- cmds[2].Parameters[0].Value = Args.Name2;
- cmds[3].Parameters[0].Value = Args.Name3;
+ var commands = batch.BatchCommands;
+ commands[0].Parameters[0].Value = Args.Name0;
+ commands[1].Parameters[0].Value = Args.Name1;
+ commands[2].Parameters[0].Value = Args.Name2;
+ commands[3].Parameters[0].Value = Args.Name3;
}
- static DbBatch? _spareBatch;
+ static NpgsqlBatch? _spareBatch;
[Benchmark]
public int AdoNetBatch()
@@ -237,47 +259,63 @@ public async ValueTask DisposeAsync()
private sealed class BasicCommand : CommandFactory
{
- private BasicCommand() { }
- public static BasicCommand Instance { get; } = new BasicCommand();
+ private readonly bool cached;
+ private BasicCommand(bool cached) => this.cached = cached;
+ public static BasicCommand NonCached { get; } = new BasicCommand(false);
+ public static BasicCommand Cached { get; } = new BasicCommand(true);
public override void AddParameters(in UnifiedCommand command, MyArgsType args)
{
- var ps = command.Parameters;
-
- var p = command.CreateParameter();
+ var p = command.AddParameter();
p.ParameterName = "Name0";
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name0);
- ps.Add(p);
- p = command.CreateParameter();
+ p = command.AddParameter();
p.ParameterName = "Name1";
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name1);
- ps.Add(p);
- p = command.CreateParameter();
+ p = command.AddParameter();
p.ParameterName = "Name2";
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name2);
- ps.Add(p);
- p = command.CreateParameter();
+ p = command.AddParameter();
p.ParameterName = "Name3";
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name3);
- ps.Add(p);
}
+
+ public override void UpdateParameters(in UnifiedBatch command, MyArgsType args)
+ {
+ var ps = command.Parameters;
+ ps[0].Value = AsValue(args.Name0);
+ ps[1].Value = AsValue(args.Name1);
+ ps[2].Value = AsValue(args.Name2);
+ ps[3].Value = AsValue(args.Name3);
+ }
+
+ private static DbCommand? _spareCommand;
+ public override bool TryRecycle(DbCommand command)
+ => cached && TryRecycle(ref _spareCommand, command);
+
+
+ public override DbCommand GetCommand(DbConnection connection, string sql, CommandType commandType, MyArgsType args)
+ => (cached ? TryReuse(ref _spareCommand, sql, commandType, args) : null)
+ ?? base.GetCommand(connection, sql, commandType, args);
}
private sealed class RewriteCommand : CommandFactory
{
- private RewriteCommand() { }
- public static RewriteCommand Instance { get; } = new RewriteCommand();
+ private readonly bool cached;
+ private RewriteCommand(bool cached) => this.cached = cached;
+ public static RewriteCommand Cached { get; } = new RewriteCommand(true);
+ public static RewriteCommand NonCached { get; } = new RewriteCommand(true);
public override void AddParameters(in UnifiedCommand command, MyArgsType args)
=> throw new NotSupportedException(); // we don't expect to get here (in reality, we would have both versions)
@@ -287,34 +325,47 @@ public override void AddParameters(in UnifiedCommand command, MyArgsType args)
public override void AddCommands(in UnifiedBatch batch, string sql, MyArgsType args)
{
// the first command is initialized automatically
- batch.CommandText = "insert into RewriteCustomers(Name) values($1)";
- var p = new NpgsqlParameter(); // batch.AddParameter(); // temp hack because CanCreateParameter => false
- batch.Parameters.Add(p);
+ batch.SetCommand("insert into RewriteCustomers(Name) values($1)");
+ var p = batch.AddParameter();
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name0);
// the fact that the NpgSql command is the same is a coincidence of the test
batch.AddCommand("insert into RewriteCustomers(Name) values($1)");
- p = new NpgsqlParameter(); // batch.AddParameter();
- batch.Parameters.Add(p);
+ p = batch.AddParameter();
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name1);
batch.AddCommand("insert into RewriteCustomers(Name) values($1)");
- p = new NpgsqlParameter(); // batch.AddParameter();
- batch.Parameters.Add(p);
+ p = batch.AddParameter();
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name2);
batch.AddCommand("insert into RewriteCustomers(Name) values($1)");
- p = new NpgsqlParameter(); // batch.AddParameter();
- batch.Parameters.Add(p);
+ p = batch.AddParameter();
p.DbType = DbType.String;
p.Size = -1;
p.Value = AsValue(args.Name3);
}
+
+ public override void UpdateParameters(in UnifiedBatch command, MyArgsType args)
+ {
+ command[0][0].Value = AsValue(args.Name0);
+ command[1][0].Value = AsValue(args.Name1);
+ command[2][0].Value = AsValue(args.Name2);
+ command[3][0].Value = AsValue(args.Name3);
+ }
+
+ private static DbBatch? _spareBatch;
+
+ public override bool TryRecycle(DbBatch batch)
+ => cached && TryRecycle(ref _spareBatch, batch);
+
+ public override DbBatch GetBatch(DbConnection connection, string sql, CommandType commandType, MyArgsType args)
+ => (cached ? TryReuse(ref _spareBatch, args) : null)
+ ?? base.GetBatch(connection, sql, commandType, args);
}
}
\ No newline at end of file
diff --git a/test/UsageBenchmark/Program.cs b/test/UsageBenchmark/Program.cs
index 0bf4a29b..d5040045 100644
--- a/test/UsageBenchmark/Program.cs
+++ b/test/UsageBenchmark/Program.cs
@@ -19,13 +19,18 @@ static async Task Main()
for (int i = 0; i < 3; i++)
{
Console.WriteLine(obj.Dapper());
- Console.WriteLine(obj.DapperAOT_Simple());
- Console.WriteLine(obj.DapperAOT_Rewrite());
+
+ Console.WriteLine(obj.DapperAOT());
+ Console.WriteLine(obj.DapperAOT_Cached());
+ Console.WriteLine(obj.DapperAOT_Batch());
+ Console.WriteLine(obj.DapperAOT_BatchCached());
+
Console.WriteLine(obj.AdoNetCommand());
Console.WriteLine(obj.AdoNetBatch());
Console.WriteLine(obj.AdoNetCommandCached());
Console.WriteLine(obj.AdoNetBatchCached());
}
+
//await using (var obj = new BatchInsertBenchmarks())
//{
// await RunAllInserts(obj, 10, false);