...
 
Commits (7)
    https://gitcode.net/jobily/Dapper/-/commit/a37b151bb745ed0763441aa50f29c504221aa7d3 auto-detect stored procedures as anything without whitespace (#1975) 2023-10-10T15:06:21+01:00 Marc Gravell marc.gravell@gmail.com * 1. auto-detect stored procedures as anything without whitespace 2. we have public fields? (hangs head in shame) * release notes * no need to use regex https://gitcode.net/jobily/Dapper/-/commit/2837480903bb8b951de4a4eff4a0c2925d42b2cb Add DuckDB tests (#1970) 2023-10-11T12:41:06+01:00 Giorgi Dalakishvili Giorgi@users.noreply.github.com * Add DuckDB tests * Update DuckDB.NET.Data.Full version https://gitcode.net/jobily/Dapper/-/commit/19193b52bb105ee2935826bd7bf26478b8ac2993 Add a setting to turn off Ole Db "smell check" (#1974) 2023-10-11T13:10:00+01:00 Giorgi Dalakishvili Giorgi@users.noreply.github.com * Add a setting to turn off Ole Db "smell check" * Rename setting, fix logic * Test for SupportLegacyParameterTokens * Fix exception type * Add release note https://gitcode.net/jobily/Dapper/-/commit/8d53acb4011c33a590dcb6fdb35ab6c48a2b456e the $ support added for DuckDB does not need to exacerbate the OLEDB problem ... 2023-10-13T07:01:56+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/b8d119fa635b535574593daf07aea831259ab698 NRT tweak on GetConstructorParameter 2023-10-13T07:06:15+01:00 Marc Gravell marc.gravell@gmail.com https://gitcode.net/jobily/Dapper/-/commit/d91f993dc6481373b2615f680baaf78252f6dbca change NRT for ITypeMap.GetConstructorParameter (#1980) 2023-10-13T07:11:10+01:00 Marc Gravell marc.gravell@gmail.com * release notes for NRT tweak * PR number https://gitcode.net/jobily/Dapper/-/commit/a93678e485b6996dbab7c06b86b5aecbcf3b9f83 move release notes to GitHub 2023-10-13T07:33:27+01:00 Marc Gravell marc.gravell@gmail.com
...@@ -48,10 +48,15 @@ internal void OnCompleted() ...@@ -48,10 +48,15 @@ internal void OnCompleted()
/// </summary> /// </summary>
public int? CommandTimeout { get; } public int? CommandTimeout { get; }
internal readonly CommandType CommandTypeDirect;
/// <summary> /// <summary>
/// The type of command that the command-text represents /// The type of command that the command-text represents
/// </summary> /// </summary>
public CommandType? CommandType { get; } #if DEBUG // prevent use in our own code
[Obsolete("Prefer " + nameof(CommandTypeDirect), true)]
#endif
public CommandType? CommandType => CommandTypeDirect;
/// <summary> /// <summary>
/// Should data be buffered before returning? /// Should data be buffered before returning?
...@@ -92,11 +97,21 @@ internal void OnCompleted() ...@@ -92,11 +97,21 @@ internal void OnCompleted()
Parameters = parameters; Parameters = parameters;
Transaction = transaction; Transaction = transaction;
CommandTimeout = commandTimeout; CommandTimeout = commandTimeout;
CommandType = commandType; CommandTypeDirect = commandType ?? InferCommandType(commandText);
Flags = flags; Flags = flags;
CancellationToken = cancellationToken; CancellationToken = cancellationToken;
static CommandType InferCommandType(string sql)
{
if (sql is null || sql.IndexOfAny(WhitespaceChars) >= 0) return System.Data.CommandType.Text;
return System.Data.CommandType.StoredProcedure;
}
} }
// if the sql contains any whitespace character (space/tab/cr/lf): interpret as ad-hoc; but "SomeName" should be treated as a stored-proc
// (note TableDirect would need to be specified explicitly, but in reality providers don't usually support TableDirect anyway)
private static readonly char[] WhitespaceChars = new char[] { ' ', '\t', '\r', '\n' };
private CommandDefinition(object? parameters) : this() private CommandDefinition(object? parameters) : this()
{ {
Parameters = parameters; Parameters = parameters;
...@@ -124,8 +139,7 @@ internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object?>? ...@@ -124,8 +139,7 @@ internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object?>?
{ {
cmd.CommandTimeout = SqlMapper.Settings.CommandTimeout.Value; cmd.CommandTimeout = SqlMapper.Settings.CommandTimeout.Value;
} }
if (CommandType.HasValue) cmd.CommandType = CommandTypeDirect;
cmd.CommandType = CommandType.Value;
paramReader?.Invoke(cmd, Parameters); paramReader?.Invoke(cmd, Parameters);
return cmd; return cmd;
} }
......
...@@ -168,7 +168,7 @@ internal static bool ShouldSetDbType(DbType dbType) ...@@ -168,7 +168,7 @@ internal static bool ShouldSetDbType(DbType dbType)
/// <param name="identity">Information about the query</param> /// <param name="identity">Information about the query</param>
protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
{ {
var literals = SqlMapper.GetLiteralTokens(identity.sql); var literals = SqlMapper.GetLiteralTokens(identity.Sql);
if (templates is not null) if (templates is not null)
{ {
......
...@@ -135,7 +135,7 @@ Dapper.SqlMapper.ITypeHandler.SetValue(System.Data.IDbDataParameter! parameter, ...@@ -135,7 +135,7 @@ Dapper.SqlMapper.ITypeHandler.SetValue(System.Data.IDbDataParameter! parameter,
Dapper.SqlMapper.ITypeMap Dapper.SqlMapper.ITypeMap
Dapper.SqlMapper.ITypeMap.FindConstructor(string![]! names, System.Type![]! types) -> System.Reflection.ConstructorInfo? Dapper.SqlMapper.ITypeMap.FindConstructor(string![]! names, System.Type![]! types) -> System.Reflection.ConstructorInfo?
Dapper.SqlMapper.ITypeMap.FindExplicitConstructor() -> System.Reflection.ConstructorInfo? Dapper.SqlMapper.ITypeMap.FindExplicitConstructor() -> System.Reflection.ConstructorInfo?
Dapper.SqlMapper.ITypeMap.GetConstructorParameter(System.Reflection.ConstructorInfo! constructor, string! columnName) -> Dapper.SqlMapper.IMemberMap! Dapper.SqlMapper.ITypeMap.GetConstructorParameter(System.Reflection.ConstructorInfo! constructor, string! columnName) -> Dapper.SqlMapper.IMemberMap?
Dapper.SqlMapper.ITypeMap.GetMember(string! columnName) -> Dapper.SqlMapper.IMemberMap? Dapper.SqlMapper.ITypeMap.GetMember(string! columnName) -> Dapper.SqlMapper.IMemberMap?
Dapper.SqlMapper.Settings Dapper.SqlMapper.Settings
Dapper.SqlMapper.StringTypeHandler<T> Dapper.SqlMapper.StringTypeHandler<T>
...@@ -152,12 +152,17 @@ override Dapper.SqlMapper.Identity.ToString() -> string! ...@@ -152,12 +152,17 @@ override Dapper.SqlMapper.Identity.ToString() -> string!
override Dapper.SqlMapper.StringTypeHandler<T>.Parse(object! value) -> T override Dapper.SqlMapper.StringTypeHandler<T>.Parse(object! value) -> T
override Dapper.SqlMapper.StringTypeHandler<T>.SetValue(System.Data.IDbDataParameter! parameter, T? value) -> void override Dapper.SqlMapper.StringTypeHandler<T>.SetValue(System.Data.IDbDataParameter! parameter, T? value) -> void
readonly Dapper.SqlMapper.Identity.commandType -> System.Data.CommandType? readonly Dapper.SqlMapper.Identity.commandType -> System.Data.CommandType?
Dapper.SqlMapper.Identity.CommandType.get -> System.Data.CommandType?
readonly Dapper.SqlMapper.Identity.connectionString -> string! readonly Dapper.SqlMapper.Identity.connectionString -> string!
readonly Dapper.SqlMapper.Identity.gridIndex -> int readonly Dapper.SqlMapper.Identity.gridIndex -> int
Dapper.SqlMapper.Identity.GridIndex.get -> int
readonly Dapper.SqlMapper.Identity.hashCode -> int readonly Dapper.SqlMapper.Identity.hashCode -> int
readonly Dapper.SqlMapper.Identity.parametersType -> System.Type? readonly Dapper.SqlMapper.Identity.parametersType -> System.Type?
Dapper.SqlMapper.Identity.ParametersType.get -> System.Type?
readonly Dapper.SqlMapper.Identity.sql -> string! readonly Dapper.SqlMapper.Identity.sql -> string!
Dapper.SqlMapper.Identity.Sql.get -> string!
readonly Dapper.SqlMapper.Identity.type -> System.Type? readonly Dapper.SqlMapper.Identity.type -> System.Type?
Dapper.SqlMapper.Identity.Type.get -> System.Type?
static Dapper.DbString.IsAnsiDefault.get -> bool static Dapper.DbString.IsAnsiDefault.get -> bool
static Dapper.DbString.IsAnsiDefault.set -> void static Dapper.DbString.IsAnsiDefault.set -> void
static Dapper.DefaultTypeMap.MatchNamesWithUnderscores.get -> bool static Dapper.DefaultTypeMap.MatchNamesWithUnderscores.get -> bool
...@@ -298,6 +303,8 @@ static Dapper.SqlMapper.SanitizeParameterValue(object? value) -> object! ...@@ -298,6 +303,8 @@ static Dapper.SqlMapper.SanitizeParameterValue(object? value) -> object!
static Dapper.SqlMapper.SetDbType(System.Data.IDataParameter! parameter, object! value) -> void static Dapper.SqlMapper.SetDbType(System.Data.IDataParameter! parameter, object! value) -> void
static Dapper.SqlMapper.Settings.ApplyNullValues.get -> bool static Dapper.SqlMapper.Settings.ApplyNullValues.get -> bool
static Dapper.SqlMapper.Settings.ApplyNullValues.set -> void static Dapper.SqlMapper.Settings.ApplyNullValues.set -> void
static Dapper.SqlMapper.Settings.SupportLegacyParameterTokens.get -> bool
static Dapper.SqlMapper.Settings.SupportLegacyParameterTokens.set -> void
static Dapper.SqlMapper.Settings.CommandTimeout.get -> int? static Dapper.SqlMapper.Settings.CommandTimeout.get -> int?
static Dapper.SqlMapper.Settings.CommandTimeout.set -> void static Dapper.SqlMapper.Settings.CommandTimeout.set -> void
static Dapper.SqlMapper.Settings.FetchSize.get -> long static Dapper.SqlMapper.Settings.FetchSize.get -> long
......
...@@ -422,7 +422,7 @@ private static DbCommand TrySetupAsyncCommand(this CommandDefinition command, ID ...@@ -422,7 +422,7 @@ private static DbCommand TrySetupAsyncCommand(this CommandDefinition command, ID
private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command) private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, Type effectiveType, CommandDefinition command)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
var cancel = command.CancellationToken; var cancel = command.CancellationToken;
...@@ -477,7 +477,7 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, ...@@ -477,7 +477,7 @@ private static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn,
private static async Task<T> QueryRowAsync<T>(this IDbConnection cnn, Row row, Type effectiveType, CommandDefinition command) private static async Task<T> QueryRowAsync<T>(this IDbConnection cnn, Row row, Type effectiveType, CommandDefinition command)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
var cancel = command.CancellationToken; var cancel = command.CancellationToken;
...@@ -652,7 +652,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD ...@@ -652,7 +652,7 @@ private static async Task<int> ExecuteMultiImplAsync(IDbConnection cnn, CommandD
private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, object? param) private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, object? param)
{ {
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader); using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader);
...@@ -930,7 +930,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini ...@@ -930,7 +930,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
private static async Task<IEnumerable<TReturn>> MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn) private static async Task<IEnumerable<TReturn>> MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(command.CommandText, command.CommandType, cnn, typeof(TFirst), param?.GetType()); var identity = new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(command.CommandText, command.CommandTypeDirect, cnn, typeof(TFirst), param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
...@@ -979,7 +979,7 @@ private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbC ...@@ -979,7 +979,7 @@ private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbC
} }
object? param = command.Parameters; object? param = command.Parameters;
var identity = new IdentityWithTypes(command.CommandText, command.CommandType, cnn, types[0], param?.GetType(), types); var identity = new IdentityWithTypes(command.CommandText, command.CommandTypeDirect, cnn, types[0], param?.GetType(), types);
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
...@@ -1029,7 +1029,7 @@ private static IEnumerable<T> ExecuteReaderSync<T>(DbDataReader reader, Func<DbD ...@@ -1029,7 +1029,7 @@ private static IEnumerable<T> ExecuteReaderSync<T>(DbDataReader reader, Func<DbD
public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, CommandDefinition command) public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, CommandDefinition command)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, typeof(GridReader), param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, typeof(GridReader), param?.GetType());
CacheInfo info = GetCacheInfo(identity, param, command.AddToCache); CacheInfo info = GetCacheInfo(identity, param, command.AddToCache);
DbCommand? cmd = null; DbCommand? cmd = null;
...@@ -1227,7 +1227,7 @@ private static async Task<DbDataReader> ExecuteWrappedReaderImplAsync(IDbConnect ...@@ -1227,7 +1227,7 @@ private static async Task<DbDataReader> ExecuteWrappedReaderImplAsync(IDbConnect
object? param = command.Parameters; object? param = command.Parameters;
if (param is not null) if (param is not null)
{ {
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param.GetType());
paramReader = GetCacheInfo(identity, command.Parameters, command.AddToCache).ParamReader; paramReader = GetCacheInfo(identity, command.Parameters, command.AddToCache).ParamReader;
} }
...@@ -1296,7 +1296,7 @@ private static IAsyncEnumerable<T> QueryUnbufferedAsync<T>(this IDbConnection cn ...@@ -1296,7 +1296,7 @@ private static IAsyncEnumerable<T> QueryUnbufferedAsync<T>(this IDbConnection cn
[EnumeratorCancellation] CancellationToken cancel) [EnumeratorCancellation] CancellationToken cancel)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader); using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader);
......
...@@ -33,7 +33,7 @@ public interface ITypeMap ...@@ -33,7 +33,7 @@ public interface ITypeMap
/// <param name="constructor">Constructor to resolve</param> /// <param name="constructor">Constructor to resolve</param>
/// <param name="columnName">DataReader column name</param> /// <param name="columnName">DataReader column name</param>
/// <returns>Mapping implementation</returns> /// <returns>Mapping implementation</returns>
IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName); IMemberMap? GetConstructorParameter(ConstructorInfo constructor, string columnName);
/// <summary> /// <summary>
/// Gets member mapping for column /// Gets member mapping for column
......
using System; using System;
using System.ComponentModel;
using System.Data; using System.Data;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
...@@ -92,6 +93,7 @@ public class Identity : IEquatable<Identity> ...@@ -92,6 +93,7 @@ public class Identity : IEquatable<Identity>
internal virtual Type GetType(int index) => throw new IndexOutOfRangeException(nameof(index)); internal virtual Type GetType(int index) => throw new IndexOutOfRangeException(nameof(index));
#pragma warning disable CS0618 // Type or member is obsolete
internal Identity ForGrid<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(Type primaryType, int gridIndex) => internal Identity ForGrid<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(Type primaryType, int gridIndex) =>
new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(sql, commandType, connectionString, primaryType, parametersType, gridIndex); new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(sql, commandType, connectionString, primaryType, parametersType, gridIndex);
...@@ -110,12 +112,14 @@ public class Identity : IEquatable<Identity> ...@@ -110,12 +112,14 @@ public class Identity : IEquatable<Identity>
/// <returns></returns> /// <returns></returns>
public Identity ForDynamicParameters(Type type) => public Identity ForDynamicParameters(Type type) =>
new Identity(sql, commandType, connectionString, this.type, type, 0, -1); new Identity(sql, commandType, connectionString, this.type, type, 0, -1);
#pragma warning restore CS0618 // Type or member is obsolete
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type? type, Type? parametersType) internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type? type, Type? parametersType)
: this(sql, commandType, connection.ConnectionString, type, parametersType, 0, 0) { /* base call */ } : this(sql, commandType, connection.ConnectionString, type, parametersType, 0, 0) { /* base call */ }
private protected Identity(string sql, CommandType? commandType, string connectionString, Type? type, Type? parametersType, int otherTypesHash, int gridIndex) private protected Identity(string sql, CommandType? commandType, string connectionString, Type? type, Type? parametersType, int otherTypesHash, int gridIndex)
{ {
#pragma warning disable CS0618 // Type or member is obsolete
this.sql = sql; this.sql = sql;
this.commandType = commandType; this.commandType = commandType;
this.connectionString = connectionString; this.connectionString = connectionString;
...@@ -133,6 +137,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti ...@@ -133,6 +137,7 @@ private protected Identity(string sql, CommandType? commandType, string connecti
hashCode = (hashCode * 23) + (connectionString is null ? 0 : connectionStringComparer.GetHashCode(connectionString)); hashCode = (hashCode * 23) + (connectionString is null ? 0 : connectionStringComparer.GetHashCode(connectionString));
hashCode = (hashCode * 23) + (parametersType?.GetHashCode() ?? 0); hashCode = (hashCode * 23) + (parametersType?.GetHashCode() ?? 0);
} }
#pragma warning restore CS0618 // Type or member is obsolete
} }
/// <summary> /// <summary>
...@@ -144,48 +149,101 @@ private protected Identity(string sql, CommandType? commandType, string connecti ...@@ -144,48 +149,101 @@ private protected Identity(string sql, CommandType? commandType, string connecti
/// <summary> /// <summary>
/// The raw SQL command. /// The raw SQL command.
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Please use " + nameof(Sql) + ". This API may be removed at a later date.")]
public readonly string sql; public readonly string sql;
/// <summary>
/// The raw SQL command.
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public string Sql => sql;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary> /// <summary>
/// The SQL command type. /// The SQL command type.
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Please use " + nameof(CommandType) + ". This API may be removed at a later date.")]
public readonly CommandType? commandType; public readonly CommandType? commandType;
/// <summary>
/// The SQL command type.
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public CommandType? CommandType => commandType;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary> /// <summary>
/// The hash code of this Identity. /// The hash code of this Identity.
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Please use " + nameof(GetHashCode) + ". This API may be removed at a later date.")]
public readonly int hashCode; public readonly int hashCode;
/// <summary> /// <summary>
/// The grid index (position in the reader) of this Identity. /// The grid index (position in the reader) of this Identity.
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Please use " + nameof(GridIndex) + ". This API may be removed at a later date.")]
public readonly int gridIndex; public readonly int gridIndex;
/// <summary> /// <summary>
/// This <see cref="Type"/> of this Identity. /// The grid index (position in the reader) of this Identity.
/// </summary> /// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public int GridIndex => gridIndex;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary>
/// The <see cref="Type"/> of this Identity.
/// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Please use " + nameof(Type) + ". This API may be removed at a later date.")]
public readonly Type? type; public readonly Type? type;
/// <summary>
/// The <see cref="Type"/> of this Identity.
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public Type? Type => type;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary> /// <summary>
/// The connection string for this Identity. /// The connection string for this Identity.
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This API may be removed at a later date.")]
public readonly string connectionString; public readonly string connectionString;
/// <summary> /// <summary>
/// The type of the parameters object for this Identity. /// The type of the parameters object for this Identity.
/// </summary> /// </summary>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Please use " + nameof(ParametersType) + ". This API may be removed at a later date.")]
public readonly Type? parametersType; public readonly Type? parametersType;
/// <summary>
/// The type of the parameters object for this Identity.
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public Type? ParametersType => parametersType;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary> /// <summary>
/// Gets the hash code for this identity. /// Gets the hash code for this identity.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
#pragma warning disable CS0618 // Type or member is obsolete
public override int GetHashCode() => hashCode; public override int GetHashCode() => hashCode;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary> /// <summary>
/// See object.ToString() /// See object.ToString()
/// </summary> /// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public override string ToString() => sql; public override string ToString() => sql;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary> /// <summary>
/// Compare 2 Identity objects /// Compare 2 Identity objects
...@@ -198,6 +256,7 @@ public bool Equals(Identity? other) ...@@ -198,6 +256,7 @@ public bool Equals(Identity? other)
if (other is null) return false; if (other is null) return false;
int typeCount; int typeCount;
#pragma warning disable CS0618 // Type or member is obsolete
return gridIndex == other.gridIndex return gridIndex == other.gridIndex
&& type == other.type && type == other.type
&& sql == other.sql && sql == other.sql
...@@ -206,6 +265,7 @@ public bool Equals(Identity? other) ...@@ -206,6 +265,7 @@ public bool Equals(Identity? other)
&& parametersType == other.parametersType && parametersType == other.parametersType
&& (typeCount = TypeCount) == other.TypeCount && (typeCount = TypeCount) == other.TypeCount
&& (typeCount == 0 || TypesEqual(this, other, typeCount)); && (typeCount == 0 || TypesEqual(this, other, typeCount));
#pragma warning restore CS0618 // Type or member is obsolete
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
......
...@@ -121,6 +121,13 @@ public static long FetchSize ...@@ -121,6 +121,13 @@ public static long FetchSize
} }
} }
/// <summary>
/// Indicates whether single-character parameter tokens (<c>?</c> etc) will be detected and used where possible;
/// this feature is not recommended and will be disabled by default in future versions;
/// where possible, prefer named parameters (<c>@yourParam</c> etc) or Dapper's "pseudo-positional" parameters (<c>?yourParam? etc</c>).
/// </summary>
public static bool SupportLegacyParameterTokens { get; set; } = true;
private static long s_FetchSize = -1; private static long s_FetchSize = -1;
} }
} }
......
...@@ -115,7 +115,7 @@ private static void PurgeQueryCacheByType(Type type) ...@@ -115,7 +115,7 @@ private static void PurgeQueryCacheByType(Type type)
{ {
foreach (var entry in _queryCache) foreach (var entry in _queryCache)
{ {
if (entry.Key.type == type) if (entry.Key.Type == type)
_queryCache.TryRemove(entry.Key, out _); _queryCache.TryRemove(entry.Key, out _);
} }
TypeDeserializerCache.Purge(type); TypeDeserializerCache.Purge(type);
...@@ -137,7 +137,9 @@ public static int GetCachedSQLCount() ...@@ -137,7 +137,9 @@ public static int GetCachedSQLCount()
/// <returns></returns> /// <returns></returns>
public static IEnumerable<Tuple<string, string, int>> GetCachedSQL(int ignoreHitCountAbove = int.MaxValue) public static IEnumerable<Tuple<string, string, int>> GetCachedSQL(int ignoreHitCountAbove = int.MaxValue)
{ {
var data = _queryCache.Select(pair => Tuple.Create(pair.Key.connectionString, pair.Key.sql, pair.Value.GetHitCount())); #pragma warning disable CS0618 // Type or member is obsolete
var data = _queryCache.Select(pair => Tuple.Create(pair.Key.connectionString, pair.Key.Sql, pair.Value.GetHitCount()));
#pragma warning restore CS0618 // Type or member is obsolete
return (ignoreHitCountAbove < int.MaxValue) return (ignoreHitCountAbove < int.MaxValue)
? data.Where(tuple => tuple.Item3 <= ignoreHitCountAbove) ? data.Where(tuple => tuple.Item3 <= ignoreHitCountAbove)
: data; : data;
...@@ -146,19 +148,19 @@ public static int GetCachedSQLCount() ...@@ -146,19 +148,19 @@ public static int GetCachedSQLCount()
/// <summary> /// <summary>
/// Deep diagnostics only: find any hash collisions in the cache /// Deep diagnostics only: find any hash collisions in the cache
/// </summary> /// </summary>
/// <returns></returns> public static IEnumerable<Tuple<int, int>> GetHashCollissions() // legacy incorrect spelling, oops
public static IEnumerable<Tuple<int, int>> GetHashCollissions()
{ {
var counts = new Dictionary<int, int>(); var counts = new Dictionary<int, int>();
foreach (var key in _queryCache.Keys) foreach (var key in _queryCache.Keys)
{ {
if (!counts.TryGetValue(key.hashCode, out int count)) var hash = key.GetHashCode();
if (!counts.TryGetValue(hash, out int count))
{ {
counts.Add(key.hashCode, 1); counts.Add(hash, 1);
} }
else else
{ {
counts[key.hashCode] = count + 1; counts[hash] = count + 1;
} }
} }
return from pair in counts return from pair in counts
...@@ -648,7 +650,7 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com ...@@ -648,7 +650,7 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
// nice and simple // nice and simple
if (param is not null) if (param is not null)
{ {
identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType()); identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param.GetType());
info = GetCacheInfo(identity, param, command.AddToCache); info = GetCacheInfo(identity, param, command.AddToCache);
} }
return ExecuteCommand(cnn, ref command, param is null ? null : info!.ParamReader); return ExecuteCommand(cnn, ref command, param is null ? null : info!.ParamReader);
...@@ -1108,7 +1110,7 @@ public static GridReader QueryMultiple(this IDbConnection cnn, string sql, objec ...@@ -1108,7 +1110,7 @@ public static GridReader QueryMultiple(this IDbConnection cnn, string sql, objec
private static GridReader QueryMultipleImpl(this IDbConnection cnn, ref CommandDefinition command) private static GridReader QueryMultipleImpl(this IDbConnection cnn, ref CommandDefinition command)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, typeof(GridReader), param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, typeof(GridReader), param?.GetType());
CacheInfo info = GetCacheInfo(identity, param, command.AddToCache); CacheInfo info = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand? cmd = null; IDbCommand? cmd = null;
...@@ -1167,7 +1169,7 @@ private static DbDataReader ExecuteReaderWithFlagsFallback(IDbCommand cmd, bool ...@@ -1167,7 +1169,7 @@ private static DbDataReader ExecuteReaderWithFlagsFallback(IDbCommand cmd, bool
private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType) private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand? cmd = null; IDbCommand? cmd = null;
...@@ -1260,7 +1262,7 @@ private static void ThrowZeroRows(Row row) ...@@ -1260,7 +1262,7 @@ private static void ThrowZeroRows(Row row)
private static T QueryRowImpl<T>(IDbConnection cnn, Row row, ref CommandDefinition command, Type effectiveType) private static T QueryRowImpl<T>(IDbConnection cnn, Row row, ref CommandDefinition command, Type effectiveType)
{ {
object? param = command.Parameters; object? param = command.Parameters;
var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, effectiveType, param?.GetType());
var info = GetCacheInfo(identity, param, command.AddToCache); var info = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand? cmd = null; IDbCommand? cmd = null;
...@@ -1549,7 +1551,7 @@ public static IEnumerable<TReturn> Query<TReturn>(this IDbConnection cnn, string ...@@ -1549,7 +1551,7 @@ public static IEnumerable<TReturn> Query<TReturn>(this IDbConnection cnn, string
private static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection? cnn, CommandDefinition command, Delegate map, string splitOn, DbDataReader? reader, Identity? identity, bool finalize) private static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection? cnn, CommandDefinition command, Delegate map, string splitOn, DbDataReader? reader, Identity? identity, bool finalize)
{ {
object? param = command.Parameters; object? param = command.Parameters;
identity ??= new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(command.CommandText, command.CommandType, cnn!, typeof(TFirst), param?.GetType()); identity ??= new Identity<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh>(command.CommandText, command.CommandTypeDirect, cnn!, typeof(TFirst), param?.GetType());
CacheInfo cinfo = GetCacheInfo(identity, param, command.AddToCache); CacheInfo cinfo = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand? ownedCommand = null; IDbCommand? ownedCommand = null;
...@@ -1620,7 +1622,7 @@ private static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection? cn ...@@ -1620,7 +1622,7 @@ private static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection? cn
} }
object? param = command.Parameters; object? param = command.Parameters;
identity ??= new IdentityWithTypes(command.CommandText, command.CommandType, cnn!, types[0], param?.GetType(), types); identity ??= new IdentityWithTypes(command.CommandText, command.CommandTypeDirect, cnn!, types[0], param?.GetType(), types);
CacheInfo cinfo = GetCacheInfo(identity, param, command.AddToCache); CacheInfo cinfo = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand? ownedCommand = null; IDbCommand? ownedCommand = null;
...@@ -1825,7 +1827,7 @@ private static CacheInfo GetCacheInfo(Identity identity, object? exampleParamete ...@@ -1825,7 +1827,7 @@ private static CacheInfo GetCacheInfo(Identity identity, object? exampleParamete
throw new InvalidOperationException("An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context"); throw new InvalidOperationException("An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context");
} }
info = new CacheInfo(); info = new CacheInfo();
if (identity.parametersType is not null) if (identity.ParametersType is not null)
{ {
Action<IDbCommand, object?> reader; Action<IDbCommand, object?> reader;
if (exampleParameters is IDynamicParameters) if (exampleParameters is IDynamicParameters)
...@@ -1842,10 +1844,10 @@ private static CacheInfo GetCacheInfo(Identity identity, object? exampleParamete ...@@ -1842,10 +1844,10 @@ private static CacheInfo GetCacheInfo(Identity identity, object? exampleParamete
} }
else else
{ {
var literals = GetLiteralTokens(identity.sql); var literals = GetLiteralTokens(identity.Sql);
reader = CreateParamInfoGenerator(identity, false, true, literals); reader = CreateParamInfoGenerator(identity, false, true, literals);
} }
if ((identity.commandType is null || identity.commandType == CommandType.Text) && ShouldPassByPosition(identity.sql)) if ((identity.CommandType is null || identity.CommandType == CommandType.Text) && ShouldPassByPosition(identity.Sql))
{ {
var tail = reader; var tail = reader;
reader = (cmd, obj) => reader = (cmd, obj) =>
...@@ -2385,7 +2387,7 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn ...@@ -2385,7 +2387,7 @@ private static IEnumerable<PropertyInfo> FilterParameters(IEnumerable<PropertyIn
} }
// look for ? / @ / : *by itself* // look for ? / @ / : *by itself*
private static readonly Regex smellsLikeOleDb = new(@"(?<![\p{L}\p{N}@_])[?@:$](?![\p{L}\p{N}@_])", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled), private static readonly Regex smellsLikeOleDb = new(@"(?<![\p{L}\p{N}@_])[?@:](?![\p{L}\p{N}@_])", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled),
literalTokens = new(@"(?<![\p{L}\p{N}_])\{=([\p{L}\p{N}_]+)\}", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled), literalTokens = new(@"(?<![\p{L}\p{N}_])\{=([\p{L}\p{N}_]+)\}", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled),
pseudoPositional = new(@"\?([\p{L}_][\p{L}\p{N}_]*)\?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled); pseudoPositional = new(@"\?([\p{L}_][\p{L}\p{N}_]*)\?", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
...@@ -2517,7 +2519,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql) ...@@ -2517,7 +2519,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
/// <param name="checkForDuplicates">Whether to check for duplicates.</param> /// <param name="checkForDuplicates">Whether to check for duplicates.</param>
/// <param name="removeUnused">Whether to remove unused parameters.</param> /// <param name="removeUnused">Whether to remove unused parameters.</param>
public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused) => public static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused) =>
CreateParamInfoGenerator(identity, checkForDuplicates, removeUnused, GetLiteralTokens(identity.sql)); CreateParamInfoGenerator(identity, checkForDuplicates, removeUnused, GetLiteralTokens(identity.Sql));
private static bool IsValueTuple(Type? type) => (type?.IsValueType == true private static bool IsValueTuple(Type? type) => (type?.IsValueType == true
&& type.FullName?.StartsWith("System.ValueTuple`", StringComparison.Ordinal) == true) && type.FullName?.StartsWith("System.ValueTuple`", StringComparison.Ordinal) == true)
...@@ -2525,18 +2527,20 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql) ...@@ -2525,18 +2527,20 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
internal static Action<IDbCommand, object?> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused, IList<LiteralToken> literals) internal static Action<IDbCommand, object?> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused, IList<LiteralToken> literals)
{ {
Type type = identity.parametersType!; Type type = identity.ParametersType!;
if (IsValueTuple(type)) if (IsValueTuple(type))
{ {
throw new NotSupportedException("ValueTuple should not be used for parameters - the language-level names are not available to use as parameter names, and it adds unnecessary boxing"); throw new NotSupportedException("ValueTuple should not be used for parameters - the language-level names are not available to use as parameter names, and it adds unnecessary boxing");
} }
bool filterParams = false; bool filterParams = removeUnused && identity.CommandType.GetValueOrDefault(CommandType.Text) == CommandType.Text;
if (removeUnused && identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text)
if (filterParams && Settings.SupportLegacyParameterTokens)
{ {
filterParams = !smellsLikeOleDb.IsMatch(identity.sql); filterParams = !smellsLikeOleDb.IsMatch(identity.Sql);
} }
var dm = new DynamicMethod("ParamInfo" + Guid.NewGuid().ToString(), null, new[] { typeof(IDbCommand), typeof(object) }, type, true); var dm = new DynamicMethod("ParamInfo" + Guid.NewGuid().ToString(), null, new[] { typeof(IDbCommand), typeof(object) }, type, true);
var il = dm.GetILGenerator(); var il = dm.GetILGenerator();
...@@ -2628,7 +2632,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql) ...@@ -2628,7 +2632,7 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
} }
if (filterParams) if (filterParams)
{ {
props = FilterParameters(props, identity.sql); props = FilterParameters(props, identity.Sql);
} }
var callOpCode = isStruct ? OpCodes.Call : OpCodes.Callvirt; var callOpCode = isStruct ? OpCodes.Call : OpCodes.Callvirt;
...@@ -2971,7 +2975,7 @@ private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition comma ...@@ -2971,7 +2975,7 @@ private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition comma
object? param = command.Parameters; object? param = command.Parameters;
if (param is not null) if (param is not null)
{ {
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param.GetType());
paramReader = GetCacheInfo(identity, command.Parameters, command.AddToCache).ParamReader; paramReader = GetCacheInfo(identity, command.Parameters, command.AddToCache).ParamReader;
} }
...@@ -3033,7 +3037,7 @@ private static DbDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefi ...@@ -3033,7 +3037,7 @@ private static DbDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefi
// nice and simple // nice and simple
if (param is not null) if (param is not null)
{ {
var identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType()); var identity = new Identity(command.CommandText, command.CommandTypeDirect, cnn, null, param.GetType());
info = GetCacheInfo(identity, param, command.AddToCache); info = GetCacheInfo(identity, param, command.AddToCache);
} }
var paramReader = info?.ParamReader; var paramReader = info?.ParamReader;
......
<Project> <Project>
<ItemGroup> <ItemGroup>
<!-- note: 6.2.0 has regressions; don't force the update --> <!-- note: 6.2.0 has regressions; don't force the update -->
<PackageVersion Include="DuckDB.NET.Data.Full" Version="0.9.0.3" />
<PackageVersion Include="EntityFramework" Version="6.1.3" /> <PackageVersion Include="EntityFramework" Version="6.1.3" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" /> <PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" /> <PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
......
...@@ -20,10 +20,21 @@ Note: to get the latest pre-release build, add ` -Pre` to the end of the command ...@@ -20,10 +20,21 @@ Note: to get the latest pre-release build, add ` -Pre` to the end of the command
## Release Notes ## Release Notes
### unreleased **RELEASE NOTE TRACKING HAS MOVED TO GITHUB**
See: https://github.com/DapperLib/Dapper/releases
Archive only (no new entries):
### 2.1.11
(note: new PRs will not be merged until they add release note wording here) (note: new PRs will not be merged until they add release note wording here)
- infer command text without any whitespace as stored-procedure (#1975 via @mgravell)
- add global `SupportLegacyParameterTokens` setting to enable or disable single-character parameter tokens (#1974 via @Giorgi)
- revert `$` addition for legacy parameter tokens (#1979 via @mgravell)
- change NRT annotation on `GetConstructorParameter` (#1980 via @mgravell, fixes #1969)
### 2.1.4 ### 2.1.4
- add untyped `GridReader.ReadUnbufferedAsync` API (#1958 via @mgravell) - add untyped `GridReader.ReadUnbufferedAsync` API (#1958 via @mgravell)
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../Dapper.ProviderTools/Dapper.ProviderTools.csproj" /> <ProjectReference Include="../../Dapper.ProviderTools/Dapper.ProviderTools.csproj" />
<ProjectReference Include="../../Dapper.SqlBuilder/Dapper.SqlBuilder.csproj" /> <ProjectReference Include="../../Dapper.SqlBuilder/Dapper.SqlBuilder.csproj" />
<PackageReference Include="DuckDB.NET.Data.Full" />
<PackageReference Include="FirebirdSql.Data.FirebirdClient" /> <PackageReference Include="FirebirdSql.Data.FirebirdClient" />
<PackageReference Include="Microsoft.Data.SqlClient" /> <PackageReference Include="Microsoft.Data.SqlClient" />
<PackageReference Include="Microsoft.Data.Sqlite" /> <PackageReference Include="Microsoft.Data.Sqlite" />
......
...@@ -1511,6 +1511,36 @@ public void Issue601_InternationalParameterNamesWork() ...@@ -1511,6 +1511,36 @@ public void Issue601_InternationalParameterNamesWork()
[FactLongRunning] [FactLongRunning]
public void TestListExpansionPadding_Disabled() => TestListExpansionPadding(false); public void TestListExpansionPadding_Disabled() => TestListExpansionPadding(false);
[Theory]
[InlineData(true)]
[InlineData(false)]
public void OleDbParamFilterFails(bool legacyParameterToken)
{
SqlMapper.PurgeQueryCache();
var oldValue = SqlMapper.Settings.SupportLegacyParameterTokens;
try
{
SqlMapper.Settings.SupportLegacyParameterTokens = legacyParameterToken;
if (legacyParameterToken) // OLE DB parameter support enabled; can false-positive
{
Assert.Throws<NotSupportedException>(() => GetValue(connection));
}
else // OLE DB parameter support disabled; more reliable
{
Assert.Equal("this ? could be awkward", GetValue(connection));
}
}
finally
{
SqlMapper.Settings.SupportLegacyParameterTokens = oldValue;
}
static string GetValue(DbConnection connection)
=> connection.QuerySingle<string>("select 'this ? could be awkward'",
new TypeWithDodgyProperties());
}
private void TestListExpansionPadding(bool enabled) private void TestListExpansionPadding(bool enabled)
{ {
bool oldVal = SqlMapper.Settings.PadListExpansions; bool oldVal = SqlMapper.Settings.PadListExpansions;
...@@ -1706,5 +1736,10 @@ class HazNullableSqlDecimal ...@@ -1706,5 +1736,10 @@ class HazNullableSqlDecimal
public int Id { get; set; } public int Id { get; set; }
public SqlDecimal? Value { get; set; } public SqlDecimal? Value { get; set; }
} }
class TypeWithDodgyProperties
{
public string Name => throw new NotSupportedException();
}
} }
} }
...@@ -92,6 +92,26 @@ public void TestIssue17648290() ...@@ -92,6 +92,26 @@ public void TestIssue17648290()
Assert.Equal("Completed successfully", p.Get<string>("ErrorDescription")); Assert.Equal("Completed successfully", p.Get<string>("ErrorDescription"));
} }
[Theory]
[InlineData(CommandType.StoredProcedure)]
[InlineData(null)] // auto
public void InferProcedure(CommandType? commandType)
{
connection.Execute("CREATE PROCEDURE #InferProcedure @id int AS BEGIN SELECT -@id END");
var result = connection.QuerySingle<int>("#InferProcedure", new { id = 42 }, commandType: commandType);
Assert.Equal(-42, result);
}
[Theory]
[InlineData(CommandType.Text)]
[InlineData(null)] // auto
public void InferNotProcedure(CommandType? commandType)
{
connection.Execute("CREATE PROCEDURE #InferNotProcedure @id int AS BEGIN SELECT -@id END");
var result = connection.QuerySingle<int>("EXEC #InferNotProcedure @id", new { id = 42 }, commandType: commandType);
Assert.Equal(-42, result);
}
[Fact] [Fact]
public void SO24605346_ProcsAndStrings() public void SO24605346_ProcsAndStrings()
{ {
......
using System;
using System.Data.Common;
using DuckDB.NET.Data;
using Xunit;
namespace Dapper.Tests
{
public class DuckDBProvider : DatabaseProvider
{
public override DbProviderFactory Factory => DuckDBClientFactory.Instance;
public override string GetConnectionString() => "Data Source=:memory:";
}
public abstract class DuckDBTypeTestBase : TestBase<DuckDBProvider>
{
protected DuckDBConnection GetDuckDBConnection(bool open = true)
=> (DuckDBConnection)(open ? Provider.GetOpenConnection() : Provider.GetClosedConnection());
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class FactDuckDBAttribute : FactAttribute
{
public override string? Skip
{
get { return unavailable ?? base.Skip; }
set { base.Skip = value; }
}
private static readonly string? unavailable;
static FactDuckDBAttribute()
{
try
{
using var _ = DatabaseProvider<DuckDBProvider>.Instance.GetOpenConnection();
}
catch (Exception ex)
{
unavailable = $"DuckDB is unavailable: {ex.Message}";
}
}
}
}
public class DuckDBTests : DuckDBTypeTestBase
{
[FactDuckDB]
public void DuckDBNamedParameter()
{
using var connection = GetDuckDBConnection();
var result = connection.QueryFirst<int>("Select $foo", new {foo = 42});
Assert.Equal(42, result);
}
[FactDuckDB]
public void DuckDBPositionalParameter()
{
using var connection = GetDuckDBConnection();
var dp = new DynamicParameters();
dp.Add("?", 42);
var result = connection.QueryFirst<int>("Select ?", dp);
Assert.Equal(42, result);
}
}
}