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
/// <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[
......@@ -896,7 +897,6 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
/// ]]>
/// </code>
/// </example>
/// </remarks>
public static IDataReader ExecuteReader(
#if CSHARP30
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
#endif
)
{
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");
}
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, true);
return ExecuteReaderImpl(cnn, ref command);
}
// nice and simple
if ((object)param != null)
{
identity = new Identity(sql, commandType, cnn, null, (object)param == null ? null : ((object)param).GetType(), null);
info = GetCacheInfo(identity);
}
return ExecuteReaderCommand(cnn, transaction, sql, (object)param == null ? null : info.ParamReader, (object)param, commandTimeout, commandType);
/// <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 IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinition command)
{
return ExecuteReaderImpl(cnn, ref command);
}
#if !CSHARP30
......@@ -1285,7 +1285,7 @@ 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, 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);
return buffered ? results.ToList() : results;
}
......@@ -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;
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
cmd = SetupCommand(cnn, transaction, sql, paramReader, obj, commandTimeout, commandType);
cmd = command.SetupCommand(cnn, paramReader);
if (wasClosed) cnn.Open();
var reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);
wasClosed = false;
......@@ -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)
{
// 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)
var dbType = param.DbType;
var val = param.Value;
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);
......
......@@ -342,5 +342,71 @@ public static async Task<GridReader> QueryMultipleAsync(this IDbConnection cnn,
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 Dapper;
using SqlMapper;
using System.Data;
namespace DapperTests_NET45
{
......@@ -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
{
public int Id { get; set; }
......
......@@ -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
{
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