Commit 046dbcf4 authored by Marc Gravell's avatar Marc Gravell

Fixes to ExecuteReader for new API; also adds ExecuteReaderAsync; also fix...

Fixes to ExecuteReader for new API; also adds ExecuteReaderAsync; also fix null parameters which broke in an unrelated pull request
parent c76483cc
...@@ -885,6 +885,7 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com ...@@ -885,6 +885,7 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
/// <remarks> /// <remarks>
/// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a <see cref="DataTable"/> /// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a <see cref="DataTable"/>
/// or <see cref="DataSet"/>. /// or <see cref="DataSet"/>.
/// </remarks>
/// <example> /// <example>
/// <code> /// <code>
/// <![CDATA[ /// <![CDATA[
...@@ -896,7 +897,6 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com ...@@ -896,7 +897,6 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
/// ]]> /// ]]>
/// </code> /// </code>
/// </example> /// </example>
/// </remarks>
public static IDataReader ExecuteReader( public static IDataReader ExecuteReader(
#if CSHARP30 #if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
...@@ -905,21 +905,21 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com ...@@ -905,21 +905,21 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
#endif #endif
) )
{ {
IEnumerable multiExec = (object)param as IEnumerable; var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
Identity identity; return ExecuteReaderImpl(cnn, ref command);
CacheInfo info = null; }
if (multiExec != null && !(multiExec is string))
{
throw new NotSupportedException("MultiExec is not supported by ExecuteReader");
}
// nice and simple /// <summary>
if ((object)param != null) /// Execute parameterized SQL and return an <see cref="IDataReader"/>
{ /// </summary>
identity = new Identity(sql, commandType, cnn, null, (object)param == null ? null : ((object)param).GetType(), null); /// <returns>An <see cref="IDataReader"/> that can be used to iterate over the results of the SQL query.</returns>
info = GetCacheInfo(identity); /// <remarks>
} /// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a <see cref="DataTable"/>
return ExecuteReaderCommand(cnn, transaction, sql, (object)param == null ? null : info.ParamReader, (object)param, commandTimeout, commandType); /// or <see cref="DataSet"/>.
/// </remarks>
public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinition command)
{
return ExecuteReaderImpl(cnn, ref command);
} }
#if !CSHARP30 #if !CSHARP30
...@@ -1285,7 +1285,7 @@ partial class DontMap { } ...@@ -1285,7 +1285,7 @@ partial class DontMap { }
static IEnumerable<TReturn> MultiMap<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>( 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) this IDbConnection cnn, string sql, Delegate map, object param, IDbTransaction transaction, bool buffered, string splitOn, int? commandTimeout, CommandType? commandType)
{ {
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered); var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered);
var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, command, map, splitOn, null, null); var results = MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(cnn, command, map, splitOn, null, null);
return buffered ? results.ToList() : results; return buffered ? results.ToList() : results;
} }
...@@ -2317,13 +2317,15 @@ private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition comma ...@@ -2317,13 +2317,15 @@ private static int ExecuteCommand(IDbConnection cnn, ref CommandDefinition comma
} }
} }
private static IDataReader ExecuteReaderCommand(IDbConnection cnn, IDbTransaction transaction, string sql, Action<IDbCommand, object> paramReader, object obj, int? commandTimeout, CommandType? commandType) private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefinition command)
{ {
Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command);
IDbCommand cmd = null; IDbCommand cmd = null;
bool wasClosed = cnn.State == ConnectionState.Closed; bool wasClosed = cnn.State == ConnectionState.Closed;
try try
{ {
cmd = SetupCommand(cnn, transaction, sql, paramReader, obj, commandTimeout, commandType); cmd = command.SetupCommand(cnn, paramReader);
if (wasClosed) cnn.Open(); if (wasClosed) cnn.Open();
var reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default); var reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);
wasClosed = false; wasClosed = false;
...@@ -2336,6 +2338,27 @@ private static IDataReader ExecuteReaderCommand(IDbConnection cnn, IDbTransactio ...@@ -2336,6 +2338,27 @@ private static IDataReader ExecuteReaderCommand(IDbConnection cnn, IDbTransactio
} }
} }
private static Action<IDbCommand, object> GetParameterReader(IDbConnection cnn, ref CommandDefinition command)
{
object param = command.Parameters;
IEnumerable multiExec = (object)param as IEnumerable;
Identity identity;
CacheInfo info = null;
if (multiExec != null && !(multiExec is string))
{
throw new NotSupportedException("MultiExec is not supported by ExecuteReader");
}
// nice and simple
if (param != null)
{
identity = new Identity(command.CommandText, command.CommandType, cnn, null, param.GetType(), null);
info = GetCacheInfo(identity);
}
var paramReader = info == null ? null : info.ParamReader;
return paramReader;
}
private static Func<IDataReader, object> GetStructDeserializer(Type type, Type effectiveType, int index) private static Func<IDataReader, object> GetStructDeserializer(Type type, Type effectiveType, int index)
{ {
// no point using special per-type handling here; it boils down to the same, plus not all are supported anyway (see: SqlDataReader.GetChar - not supported!) // no point using special per-type handling here; it boils down to the same, plus not all are supported anyway (see: SqlDataReader.GetChar - not supported!)
...@@ -3325,7 +3348,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -3325,7 +3348,7 @@ protected void AddParameters(IDbCommand command, SqlMapper.Identity identity)
var dbType = param.DbType; var dbType = param.DbType;
var val = param.Value; var val = param.Value;
string name = Clean(param.Name); string name = Clean(param.Name);
var isCustomQueryParameter = typeof(SqlMapper.ICustomQueryParameter).IsAssignableFrom(val.GetType()); var isCustomQueryParameter = val is SqlMapper.ICustomQueryParameter;
if (dbType == null && val != null && !isCustomQueryParameter) dbType = SqlMapper.LookupDbType(val.GetType(), name); if (dbType == null && val != null && !isCustomQueryParameter) dbType = SqlMapper.LookupDbType(val.GetType(), name);
......
...@@ -342,5 +342,71 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn, ...@@ -342,5 +342,71 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
throw; throw;
} }
} }
/// <summary>
/// Execute parameterized SQL and return an <see cref="IDataReader"/>
/// </summary>
/// <returns>An <see cref="IDataReader"/> that can be used to iterate over the results of the SQL query.</returns>
/// <remarks>
/// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a <see cref="DataTable"/>
/// or <see cref="DataSet"/>.
/// </remarks>
/// <example>
/// <code>
/// <![CDATA[
/// DataTable table = new DataTable("MyTable");
/// using (var reader = ExecuteReader(cnn, sql, param))
/// {
/// table.Load(reader);
/// }
/// ]]>
/// </code>
/// </example>
public static Task<IDataReader> ExecuteReaderAsync(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
#endif
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
return ExecuteReaderImplAsync(cnn, command);
}
/// <summary>
/// Execute parameterized SQL and return an <see cref="IDataReader"/>
/// </summary>
/// <returns>An <see cref="IDataReader"/> that can be used to iterate over the results of the SQL query.</returns>
/// <remarks>
/// This is typically used when the results of a query are not processed by Dapper, for example, used to fill a <see cref="DataTable"/>
/// or <see cref="DataSet"/>.
/// </remarks>
public static Task<IDataReader> ExecuteReaderAsync(this IDbConnection cnn, CommandDefinition command)
{
return ExecuteReaderImplAsync(cnn, command);
}
private static async Task<IDataReader> ExecuteReaderImplAsync(IDbConnection cnn, CommandDefinition command)
{
Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command);
DbCommand cmd = null;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
cmd = (DbCommand)command.SetupCommand(cnn, paramReader);
if (wasClosed) await ((DbConnection)cnn).OpenAsync().ConfigureAwait(false);
var reader = await cmd.ExecuteReaderAsync(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default, command.CancellationToken).ConfigureAwait(false);
wasClosed = false;
return reader;
}
finally
{
if (wasClosed) cnn.Close();
if (cmd != null) cmd.Dispose();
}
}
} }
} }
\ No newline at end of file
using System.Linq; using System.Linq;
using Dapper; using Dapper;
using SqlMapper; using SqlMapper;
using System.Data;
namespace DapperTests_NET45 namespace DapperTests_NET45
{ {
...@@ -118,6 +119,35 @@ public void TestMultiClosedConnAsync() ...@@ -118,6 +119,35 @@ public void TestMultiClosedConnAsync()
} }
} }
public void ExecuteReaderOpenAsync()
{
using (var conn = Program.GetOpenConnection())
{
var dt = new DataTable();
dt.Load(conn.ExecuteReaderAsync("select 3 as [three], 4 as [four]").Result);
dt.Columns.Count.IsEqualTo(2);
dt.Columns[0].ColumnName.IsEqualTo("three");
dt.Columns[1].ColumnName.IsEqualTo("four");
dt.Rows.Count.IsEqualTo(1);
((int)dt.Rows[0][0]).IsEqualTo(3);
((int)dt.Rows[0][1]).IsEqualTo(4);
}
}
public void ExecuteReaderClosedAsync()
{
using (var conn = Program.GetClosedConnection())
{
var dt = new DataTable();
dt.Load(conn.ExecuteReaderAsync("select 3 as [three], 4 as [four]").Result);
dt.Columns.Count.IsEqualTo(2);
dt.Columns[0].ColumnName.IsEqualTo("three");
dt.Columns[1].ColumnName.IsEqualTo("four");
dt.Rows.Count.IsEqualTo(1);
((int)dt.Rows[0][0]).IsEqualTo(3);
((int)dt.Rows[0][1]).IsEqualTo(4);
}
}
class Product class Product
{ {
public int Id { get; set; } public int Id { get; set; }
......
...@@ -978,6 +978,19 @@ public void TestFieldsAndPrivates() ...@@ -978,6 +978,19 @@ public void TestFieldsAndPrivates()
} }
public void ExecuteReader()
{
var dt = new DataTable();
dt.Load(connection.ExecuteReader("select 3 as [three], 4 as [four]"));
dt.Columns.Count.IsEqualTo(2);
dt.Columns[0].ColumnName.IsEqualTo("three");
dt.Columns[1].ColumnName.IsEqualTo("four");
dt.Rows.Count.IsEqualTo(1);
((int)dt.Rows[0][0]).IsEqualTo(3);
((int)dt.Rows[0][1]).IsEqualTo(4);
}
private class TestFieldCaseAndPrivatesEntity private class TestFieldCaseAndPrivatesEntity
{ {
public int a { get; set; } public int a { get; set; }
......
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