Commit 87f71d3d authored by Marc Gravell's avatar Marc Gravell

1.22: support literal replacement on enumerable values for "in" etc

parent 8a875b87
......@@ -32,5 +32,5 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.21.0.0")]
[assembly: AssemblyFileVersion("1.21.0.0")]
[assembly: AssemblyVersion("1.22.0.0")]
[assembly: AssemblyFileVersion("1.22.0.0")]
......@@ -22,6 +22,26 @@
namespace Dapper
{
/// <summary>
/// Additional state flags that control command behaviour
/// </summary>
[Flags]
public enum CommandFlags
{
/// <summary>
/// No additonal flags
/// </summary>
None = 0,
/// <summary>
/// Should data be buffered before returning?
/// </summary>
Buffered = 1,
/// <summary>
/// Can async queries be pipelined?
/// </summary>
Pipelined = 2,
}
/// <summary>
/// Represents the key aspects of a sql operation
/// </summary>
......@@ -32,7 +52,7 @@ public struct CommandDefinition
private readonly IDbTransaction transaction;
private readonly int? commandTimeout;
private readonly CommandType? commandType;
private readonly bool buffered;
private readonly CommandFlags flags;
......@@ -60,17 +80,27 @@ public struct CommandDefinition
/// <summary>
/// Should data be buffered before returning?
/// </summary>
public bool Buffered { get { return buffered; } }
public bool Buffered { get { return (flags & CommandFlags.Buffered) != 0; } }
/// <summary>
/// Additional state flags against this command
/// </summary>
public CommandFlags Flags { get { return flags; } }
/// <summary>
/// Can async queries be pipelined?
/// </summary>
public bool Pipelined { get { return (flags & CommandFlags.Pipelined) != 0; } }
/// <summary>
/// Initialize the command definition
/// </summary>
#if CSHARP30
public CommandDefinition(string commandText, object parameters, IDbTransaction transaction, int? commandTimeout,
CommandType? commandType, bool buffered)
CommandType? commandType, CommandFlags flags)
#else
public CommandDefinition(string commandText, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null,
CommandType? commandType = null, bool buffered = true
CommandType? commandType = null, CommandFlags flags = CommandFlags.Buffered
#if ASYNC
, CancellationToken cancellationToken = default(CancellationToken)
#endif
......@@ -82,7 +112,7 @@ public struct CommandDefinition
this.transaction = transaction;
this.commandTimeout = commandTimeout;
this.commandType = commandType;
this.buffered = buffered;
this.flags = flags;
#if ASYNC
this.cancellationToken = cancellationToken;
#endif
......@@ -96,6 +126,7 @@ public struct CommandDefinition
public CancellationToken CancellationToken { get { return cancellationToken; } }
#endif
internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> paramReader)
{
var cmd = cnn.CreateCommand();
......@@ -834,7 +865,7 @@ public static GridReader QueryMultiple(this IDbConnection cnn, string sql, objec
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return ExecuteImpl(cnn, ref command);
}
/// <summary>
......@@ -923,7 +954,7 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return ExecuteReaderImpl(cnn, ref command);
}
......@@ -1005,7 +1036,7 @@ public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dyn
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered);
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
var data = QueryImpl<T>(cnn, command);
return command.Buffered ? data.ToList() : data;
}
......@@ -1036,7 +1067,7 @@ public static IEnumerable<T> Query<T>(this IDbConnection cnn, CommandDefinition
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return QueryMultipleImpl(cnn, ref command);
}
/// <summary>
......@@ -1303,12 +1334,11 @@ partial class DontMap { }
static IEnumerable<TReturn> MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(
this IDbConnection cnn, string sql, Delegate map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered);
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, command, map, splitOn, null, null);
return buffered ? results.ToList() : results;
}
static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, CommandDefinition command, Delegate map, string splitOn, IDataReader reader, Identity identity)
{
object param = command.Parameters;
......@@ -2158,10 +2188,30 @@ public static string Format(object value)
case TypeCode.Decimal:
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
default:
if(value is IEnumerable && !(value is string))
{
var sb = new StringBuilder();
bool first = true;
foreach (object subval in (IEnumerable)value)
{
sb.Append(first ? '(' : ',').Append(Format(subval));
first = false;
}
if(first)
{
return "(select null where 1=0)";
}
else
{
return sb.Append(')').ToString();
}
}
throw new NotSupportedException(value.GetType().Name);
}
}
}
internal static void ReplaceLiterals(IParameterLookup parameters, IDbCommand command, IList<LiteralToken> tokens)
{
var sql = command.CommandText;
......@@ -2741,8 +2791,8 @@ public static void SetTypeMap(Type type, ITypeMap map)
var ctor = typeMap.FindConstructor(names, types);
if (ctor == null)
{
string proposedTypes = "(" + String.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")";
throw new InvalidOperationException(String.Format("A parameterless default constructor or one matching signature {0} is required for {1} materialization", proposedTypes, type.FullName));
string proposedTypes = "(" + string.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")";
throw new InvalidOperationException(string.Format("A parameterless default constructor or one matching signature {0} is required for {1} materialization", proposedTypes, type.FullName));
}
if (ctor.GetParameters().Length == 0)
......
......@@ -18,7 +18,7 @@ public static partial class SqlMapper
/// </summary>
public static Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
return QueryAsync<T>(cnn, new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true, default(CancellationToken)));
return QueryAsync<T>(cnn, new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
}
/// <summary>
......@@ -52,7 +52,7 @@ public static async Task<IEnumerable<T>> QueryAsync<T>(this IDbConnection cnn, C
/// </summary>
public static Task<int> ExecuteAsync(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
return ExecuteAsync(cnn, new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true, default(CancellationToken)));
return ExecuteAsync(cnn, new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered, default(CancellationToken)));
}
/// <summary>
......@@ -146,7 +146,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
return MultiMapAsync<TFirst, TSecond, DontMap, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn,
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered, default(CancellationToken)), map, splitOn);
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
}
/// <summary>
......@@ -185,7 +185,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
return MultiMapAsync<TFirst, TSecond, TThird, DontMap, DontMap, DontMap, DontMap, TReturn>(cnn,
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered, default(CancellationToken)), map, splitOn);
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
}
/// <summary>
......@@ -226,7 +226,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, DontMap, DontMap, DontMap, TReturn>(cnn,
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered, default(CancellationToken)), map, splitOn);
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
}
/// <summary>
......@@ -253,7 +253,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, DontMap, DontMap, TReturn>(cnn,
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered, default(CancellationToken)), map, splitOn);
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
}
/// <summary>
......@@ -270,7 +270,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, DontMap, TReturn>(cnn,
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered, default(CancellationToken)), map, splitOn);
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
}
/// <summary>
......@@ -287,7 +287,7 @@ private static async Task<int> ExecuteImplAsync(IDbConnection cnn, CommandDefini
public static Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(this IDbConnection cnn, string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
return MultiMapAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn,
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered, default(CancellationToken)), map, splitOn);
new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default(CancellationToken)), map, splitOn);
}
/// <summary>
......@@ -348,7 +348,7 @@ private static IEnumerable<T> ExecuteReader<T>(IDataReader reader, Identity iden
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return QueryMultipleAsync(cnn, command);
}
/// <summary>
......@@ -421,7 +421,7 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return ExecuteReaderImplAsync(cnn, command);
}
......
......@@ -31,5 +31,5 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.21.0.0")]
[assembly: AssemblyFileVersion("1.21.0.0")]
[assembly: AssemblyVersion("1.22.0.0")]
[assembly: AssemblyFileVersion("1.22.0.0")]
......@@ -31,5 +31,5 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.21.0.0")]
[assembly: AssemblyFileVersion("1.21.0.0")]
[assembly: AssemblyVersion("1.22.0.0")]
[assembly: AssemblyFileVersion("1.22.0.0")]
......@@ -31,5 +31,5 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.21.0.0")]
[assembly: AssemblyFileVersion("1.21.0.0")]
[assembly: AssemblyVersion("1.22.0.0")]
[assembly: AssemblyFileVersion("1.22.0.0")]
......@@ -31,5 +31,5 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.21.0.0")]
[assembly: AssemblyFileVersion("1.21.0.0")]
[assembly: AssemblyVersion("1.22.0.0")]
[assembly: AssemblyFileVersion("1.22.0.0")]
......@@ -31,5 +31,5 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.21.0.0")]
[assembly: AssemblyFileVersion("1.21.0.0")]
[assembly: AssemblyVersion("1.22.0.0")]
[assembly: AssemblyFileVersion("1.22.0.0")]
......@@ -191,6 +191,22 @@ private void LiteralReplacementDynamic(IDbConnection connection)
count.IsEqualTo(1);
}
public void LiteralIn()
{
using (var connection = Program.GetOpenConnection())
{
connection.ExecuteAsync("create table #literalin(id int not null);").Wait();
connection.ExecuteAsync("insert #literalin (id) values (@id)", new[] {
new { id = 1 },
new { id = 2 },
new { id = 3 },
}).Wait();
var count = connection.QueryAsync<int>("select count(1) from #literalin where id in {=ids}",
new { ids = new[] { 1, 3, 4 } }).Result.Single();
count.IsEqualTo(2);
}
}
class Product
{
public int Id { get; set; }
......
......@@ -31,5 +31,5 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.21.0.0")]
[assembly: AssemblyFileVersion("1.21.0.0")]
[assembly: AssemblyVersion("1.22.0.0")]
[assembly: AssemblyFileVersion("1.22.0.0")]
......@@ -2718,6 +2718,19 @@ public void LiteralReplacementDynamicEnumAndString()
y.Equals(123.45M);
}
public void LiteralIn()
{
connection.Execute("create table #literalin(id int not null);");
connection.Execute("insert #literalin (id) values (@id)", new[] {
new { id = 1 },
new { id = 2 },
new { id = 3 },
});
var count = connection.Query<int>("select count(1) from #literalin where id in {=ids}",
new { ids = new[] { 1, 3, 4 } }).Single();
count.IsEqualTo(2);
}
public void TestProcedureWithTimeParameter()
{
......
......@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata schemaVersion="2">
<id>Dapper</id>
<version>1.21</version>
<version>1.22</version>
<title>Dapper dot net</title>
<authors>Sam Saffron,Marc Gravell</authors>
<owners>Sam Saffron,Marc Gravell</owners>
......@@ -19,6 +19,7 @@
<frameworkAssembly assemblyName="Microsoft.CSharp" targetFramework=".NETFramework4.0-Client, .NETFramework4.0" />
</frameworkAssemblies>
<releaseNotes>
* 1.22 - Literal support now extends to enumerable types (for "in" etc usage); move to command-flags model for "buffered" etc
* 1.21 - Limit literals to numeric types; for enums, use value not name
* 1.20 - Improved async support in .NET 4.5 (lots of contributions from users here, including JasonPunyon, kwaclaw, tugberkugurlu, and mgravell);
* Now supports literal replacement via {=foo}; new ExecuteReader method (via JJoe2); support for ICustomQueryParameter
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment