Commit d0f880d1 authored by Marc Gravell's avatar Marc Gravell

1.30: better async cancellation

parent d3afa33f
......@@ -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.29.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")]
[assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.30.0.0")]
......@@ -1231,7 +1231,7 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
)
{
var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
return ExecuteReaderImpl(cnn, ref command);
return ExecuteReaderImpl(cnn, ref command, CommandBehavior.Default);
}
/// <summary>
......@@ -1244,7 +1244,19 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
/// </remarks>
public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinition command)
{
return ExecuteReaderImpl(cnn, ref command);
return ExecuteReaderImpl(cnn, ref command, CommandBehavior.Default);
}
/// <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, CommandBehavior commandBehavior)
{
return ExecuteReaderImpl(cnn, ref command, commandBehavior);
}
#if !CSHARP30
......@@ -1385,7 +1397,7 @@ private static GridReader QueryMultipleImpl(this IDbConnection cnn, ref CommandD
{
if (wasClosed) cnn.Open();
cmd = command.SetupCommand(cnn, info.ParamReader);
reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);
reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
var result = new GridReader(cmd, reader, identity);
wasClosed = false; // *if* the connection was closed and we got this far, then we now have a reader
......@@ -1423,7 +1435,7 @@ private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefini
cmd = command.SetupCommand(cnn, info.ParamReader);
if (wasClosed) cnn.Open();
reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);
reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
wasClosed = false; // *if* the connection was closed and we got this far, then we now have a reader
// with the CloseConnection flag, so the reader will deal with the connection; we
// still need something in the "finally" to ensure that broken SQL still results
......@@ -1657,7 +1669,7 @@ partial class DontMap { }
{
ownedCommand = command.SetupCommand(cnn, cinfo.ParamReader);
if (wasClosed) cnn.Open();
ownedReader = ownedCommand.ExecuteReader();
ownedReader = ownedCommand.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
reader = ownedReader;
}
DeserializerState deserializer = default(DeserializerState);
......@@ -3008,7 +3020,7 @@ private static T ExecuteScalarImpl<T>(IDbConnection cnn, ref CommandDefinition c
return Parse<T>(result);
}
private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefinition command)
private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefinition command, CommandBehavior commandBehavior)
{
Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command);
......@@ -3018,8 +3030,9 @@ private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefin
{
cmd = command.SetupCommand(cnn, paramReader);
if (wasClosed) cnn.Open();
var reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);
wasClosed = false;
if (wasClosed) commandBehavior |= CommandBehavior.CloseConnection;
var reader = cmd.ExecuteReader(commandBehavior);
wasClosed = false; // don't dispose before giving it to them!
return reader;
}
finally
......@@ -3733,7 +3746,7 @@ internal GridReader(IDbCommand command, IDataReader reader, Identity identity)
/// </summary>
public IEnumerable<dynamic> Read(bool buffered = true)
{
return Read<DapperRow>(buffered);
return ReadImpl<dynamic>(typeof(DapperRow), buffered);
}
#endif
......@@ -3755,21 +3768,7 @@ public IEnumerable<T> Read<T>(bool buffered)
public IEnumerable<T> Read<T>(bool buffered = true)
#endif
{
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
if (consumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
var typedIdentity = identity.ForGrid(typeof(T), gridIndex);
CacheInfo cache = GetCacheInfo(typedIdentity, null, true);
var deserializer = cache.Deserializer;
int hash = GetColumnHash(reader);
if (deserializer.Func == null || deserializer.Hash != hash)
{
deserializer = new DeserializerState(hash, GetDeserializer(typeof(T), reader, 0, -1, false));
cache.Deserializer = deserializer;
}
consumed = true;
var result = ReadDeferred<T>(gridIndex, deserializer.Func, typedIdentity);
return buffered ? result.ToList() : result;
return ReadImpl<T>(typeof(T), buffered);
}
/// <summary>
......@@ -3782,6 +3781,11 @@ public IEnumerable<object> Read(Type type, bool buffered = true)
#endif
{
if (type == null) throw new ArgumentNullException("type");
return ReadImpl<object>(type, buffered);
}
private IEnumerable<T> ReadImpl<T>(Type type, bool buffered)
{
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed");
if (consumed) throw new InvalidOperationException("Query results must be consumed in the correct order, and each result can only be consumed once");
var typedIdentity = identity.ForGrid(type, gridIndex);
......@@ -3795,10 +3799,11 @@ public IEnumerable<object> Read(Type type, bool buffered = true)
cache.Deserializer = deserializer;
}
consumed = true;
var result = ReadDeferred<object>(gridIndex, deserializer.Func, typedIdentity);
var result = ReadDeferred<T>(gridIndex, deserializer.Func, typedIdentity);
return buffered ? result.ToList() : result;
}
private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Delegate func, string splitOn)
{
var identity = this.identity.ForGrid(typeof(TReturn), new Type[] {
......@@ -3965,7 +3970,6 @@ private void NextResult()
Dispose();
}
}
/// <summary>
/// Dispose the grid, closing and disposing both the underlying reader and command.
......
This diff is collapsed.
......@@ -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.29.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")]
[assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.30.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.29.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")]
[assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.30.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.29.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")]
[assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.30.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.29.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")]
[assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.30.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.29.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")]
[assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.30.0.0")]
......@@ -4,6 +4,9 @@
using System.Data;
using System.Diagnostics;
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;
namespace DapperTests_NET45
{
......@@ -18,6 +21,35 @@ public void TestBasicStringUsageAsync()
arr.IsSequenceEqualTo(new[] { "abc", "def" });
}
}
public void TestBasicStringUsageAsyncNonBuffered()
{
using (var connection = Program.GetOpenConnection())
{
var query = connection.QueryAsync<string>(new CommandDefinition("select 'abc' as [Value] union all select @txt", new { txt = "def" }, flags: CommandFlags.None));
var arr = query.Result.ToArray();
arr.IsSequenceEqualTo(new[] { "abc", "def" });
}
}
public void TestLongOperationWithCancellation()
{
using(var connection = Program.GetClosedConnection())
{
CancellationTokenSource cancel = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var task = connection.QueryAsync<int>(new CommandDefinition("waitfor delay '00:00:10';select 1", cancellationToken: cancel.Token));
try
{
if (!task.Wait(TimeSpan.FromSeconds(7)))
{
throw new TimeoutException(); // should have cancelled
}
}
catch (AggregateException agg)
{
(agg.InnerException is SqlException).IsTrue();
}
}
}
public void TestBasicStringUsageClosedAsync()
{
......@@ -29,6 +61,16 @@ public void TestBasicStringUsageClosedAsync()
}
}
public void TestQueryDynamicAsync()
{
using (var connection = Program.GetClosedConnection())
{
var row = connection.QueryAsync("select 'abc' as [Value]").Result.Single();
string value = row.Value;
value.IsEqualTo("abc");
}
}
public void TestClassWithStringUsageAsync()
{
using (var connection = Program.GetOpenConnection())
......@@ -104,8 +146,8 @@ public void TestMultiAsync()
{
using (Dapper.SqlMapper.GridReader multi = conn.QueryMultipleAsync("select 1; select 2").Result)
{
multi.Read<int>().Single().IsEqualTo(1);
multi.Read<int>().Single().IsEqualTo(2);
multi.ReadAsync<int>().Result.Single().IsEqualTo(1);
multi.ReadAsync<int>().Result.Single().IsEqualTo(2);
}
}
}
......@@ -115,8 +157,8 @@ public void TestMultiClosedConnAsync()
{
using (Dapper.SqlMapper.GridReader multi = conn.QueryMultipleAsync("select 1; select 2").Result)
{
multi.Read<int>().Single().IsEqualTo(1);
multi.Read<int>().Single().IsEqualTo(2);
multi.ReadAsync<int>().Result.Single().IsEqualTo(1);
multi.ReadAsync<int>().Result.Single().IsEqualTo(2);
}
}
}
......
......@@ -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.29.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")]
[assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.30.0.0")]
......@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata schemaVersion="2">
<id>Dapper</id>
<version>1.29</version>
<version>1.30</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.30 - Better async cancellation
* 1.29 - Make underscore name matching optional (opt-in) - this can be a breaking change for some people
* 1.28 - Much better numeric type conversion; fix for large oracle strings; map Foo_Bar to FooBar (etc); ExecuteScalar added; stability fixes
* 1.27 - Fixes for type-handler parse; ensure type-handlers get last dibs on configuring parameters
......
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