Commit d0f880d1 authored by Marc Gravell's avatar Marc Gravell

1.30: better async cancellation

parent d3afa33f
...@@ -32,5 +32,5 @@ ...@@ -32,5 +32,5 @@
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.29.0.0")] [assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")] [assembly: AssemblyFileVersion("1.30.0.0")]
...@@ -1231,7 +1231,7 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com ...@@ -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); 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> /// <summary>
...@@ -1244,7 +1244,19 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com ...@@ -1244,7 +1244,19 @@ private static int ExecuteImpl(this IDbConnection cnn, ref CommandDefinition com
/// </remarks> /// </remarks>
public static IDataReader ExecuteReader(this IDbConnection cnn, CommandDefinition command) 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 #if !CSHARP30
...@@ -1385,7 +1397,7 @@ private static GridReader QueryMultipleImpl(this IDbConnection cnn, ref CommandD ...@@ -1385,7 +1397,7 @@ private static GridReader QueryMultipleImpl(this IDbConnection cnn, ref CommandD
{ {
if (wasClosed) cnn.Open(); if (wasClosed) cnn.Open();
cmd = command.SetupCommand(cnn, info.ParamReader); 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); 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 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 ...@@ -1423,7 +1435,7 @@ private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefini
cmd = command.SetupCommand(cnn, info.ParamReader); cmd = command.SetupCommand(cnn, info.ParamReader);
if (wasClosed) cnn.Open(); 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 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 // 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 // still need something in the "finally" to ensure that broken SQL still results
...@@ -1657,7 +1669,7 @@ partial class DontMap { } ...@@ -1657,7 +1669,7 @@ partial class DontMap { }
{ {
ownedCommand = command.SetupCommand(cnn, cinfo.ParamReader); ownedCommand = command.SetupCommand(cnn, cinfo.ParamReader);
if (wasClosed) cnn.Open(); if (wasClosed) cnn.Open();
ownedReader = ownedCommand.ExecuteReader(); ownedReader = ownedCommand.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
reader = ownedReader; reader = ownedReader;
} }
DeserializerState deserializer = default(DeserializerState); DeserializerState deserializer = default(DeserializerState);
...@@ -3008,7 +3020,7 @@ private static T ExecuteScalarImpl<T>(IDbConnection cnn, ref CommandDefinition c ...@@ -3008,7 +3020,7 @@ private static T ExecuteScalarImpl<T>(IDbConnection cnn, ref CommandDefinition c
return Parse<T>(result); 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); Action<IDbCommand, object> paramReader = GetParameterReader(cnn, ref command);
...@@ -3018,8 +3030,9 @@ private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefin ...@@ -3018,8 +3030,9 @@ private static IDataReader ExecuteReaderImpl(IDbConnection cnn, ref CommandDefin
{ {
cmd = command.SetupCommand(cnn, paramReader); cmd = command.SetupCommand(cnn, paramReader);
if (wasClosed) cnn.Open(); if (wasClosed) cnn.Open();
var reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default); if (wasClosed) commandBehavior |= CommandBehavior.CloseConnection;
wasClosed = false; var reader = cmd.ExecuteReader(commandBehavior);
wasClosed = false; // don't dispose before giving it to them!
return reader; return reader;
} }
finally finally
...@@ -3733,7 +3746,7 @@ internal GridReader(IDbCommand command, IDataReader reader, Identity identity) ...@@ -3733,7 +3746,7 @@ internal GridReader(IDbCommand command, IDataReader reader, Identity identity)
/// </summary> /// </summary>
public IEnumerable<dynamic> Read(bool buffered = true) public IEnumerable<dynamic> Read(bool buffered = true)
{ {
return Read<DapperRow>(buffered); return ReadImpl<dynamic>(typeof(DapperRow), buffered);
} }
#endif #endif
...@@ -3755,21 +3768,7 @@ public IEnumerable<T> Read<T>(bool buffered) ...@@ -3755,21 +3768,7 @@ public IEnumerable<T> Read<T>(bool buffered)
public IEnumerable<T> Read<T>(bool buffered = true) public IEnumerable<T> Read<T>(bool buffered = true)
#endif #endif
{ {
if (reader == null) throw new ObjectDisposedException(GetType().FullName, "The reader has been disposed; this can happen after all data has been consumed"); return ReadImpl<T>(typeof(T), buffered);
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;
} }
/// <summary> /// <summary>
...@@ -3782,6 +3781,11 @@ public IEnumerable<object> Read(Type type, bool buffered = true) ...@@ -3782,6 +3781,11 @@ public IEnumerable<object> Read(Type type, bool buffered = true)
#endif #endif
{ {
if (type == null) throw new ArgumentNullException("type"); 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 (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"); 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); var typedIdentity = identity.ForGrid(type, gridIndex);
...@@ -3795,10 +3799,11 @@ public IEnumerable<object> Read(Type type, bool buffered = true) ...@@ -3795,10 +3799,11 @@ public IEnumerable<object> Read(Type type, bool buffered = true)
cache.Deserializer = deserializer; cache.Deserializer = deserializer;
} }
consumed = true; consumed = true;
var result = ReadDeferred<object>(gridIndex, deserializer.Func, typedIdentity); var result = ReadDeferred<T>(gridIndex, deserializer.Func, typedIdentity);
return buffered ? result.ToList() : result; return buffered ? result.ToList() : result;
} }
private IEnumerable<TReturn> MultiReadInternal<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(Delegate func, string splitOn) 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[] { var identity = this.identity.ForGrid(typeof(TReturn), new Type[] {
...@@ -3965,7 +3970,6 @@ private void NextResult() ...@@ -3965,7 +3970,6 @@ private void NextResult()
Dispose(); Dispose();
} }
} }
/// <summary> /// <summary>
/// Dispose the grid, closing and disposing both the underlying reader and command. /// Dispose the grid, closing and disposing both the underlying reader and command.
......
This diff is collapsed.
...@@ -31,5 +31,5 @@ ...@@ -31,5 +31,5 @@
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.29.0.0")] [assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")] [assembly: AssemblyFileVersion("1.30.0.0")]
...@@ -31,5 +31,5 @@ ...@@ -31,5 +31,5 @@
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.29.0.0")] [assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")] [assembly: AssemblyFileVersion("1.30.0.0")]
...@@ -31,5 +31,5 @@ ...@@ -31,5 +31,5 @@
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.29.0.0")] [assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")] [assembly: AssemblyFileVersion("1.30.0.0")]
...@@ -31,5 +31,5 @@ ...@@ -31,5 +31,5 @@
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.29.0.0")] [assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")] [assembly: AssemblyFileVersion("1.30.0.0")]
...@@ -31,5 +31,5 @@ ...@@ -31,5 +31,5 @@
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.29.0.0")] [assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")] [assembly: AssemblyFileVersion("1.30.0.0")]
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
using System.Data; using System.Data;
using System.Diagnostics; using System.Diagnostics;
using System; using System;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;
namespace DapperTests_NET45 namespace DapperTests_NET45
{ {
...@@ -18,6 +21,35 @@ public void TestBasicStringUsageAsync() ...@@ -18,6 +21,35 @@ public void TestBasicStringUsageAsync()
arr.IsSequenceEqualTo(new[] { "abc", "def" }); 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() public void TestBasicStringUsageClosedAsync()
{ {
...@@ -29,6 +61,16 @@ 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() public void TestClassWithStringUsageAsync()
{ {
using (var connection = Program.GetOpenConnection()) using (var connection = Program.GetOpenConnection())
...@@ -104,8 +146,8 @@ public void TestMultiAsync() ...@@ -104,8 +146,8 @@ public void TestMultiAsync()
{ {
using (Dapper.SqlMapper.GridReader multi = conn.QueryMultipleAsync("select 1; select 2").Result) using (Dapper.SqlMapper.GridReader multi = conn.QueryMultipleAsync("select 1; select 2").Result)
{ {
multi.Read<int>().Single().IsEqualTo(1); multi.ReadAsync<int>().Result.Single().IsEqualTo(1);
multi.Read<int>().Single().IsEqualTo(2); multi.ReadAsync<int>().Result.Single().IsEqualTo(2);
} }
} }
} }
...@@ -115,8 +157,8 @@ public void TestMultiClosedConnAsync() ...@@ -115,8 +157,8 @@ public void TestMultiClosedConnAsync()
{ {
using (Dapper.SqlMapper.GridReader multi = conn.QueryMultipleAsync("select 1; select 2").Result) using (Dapper.SqlMapper.GridReader multi = conn.QueryMultipleAsync("select 1; select 2").Result)
{ {
multi.Read<int>().Single().IsEqualTo(1); multi.ReadAsync<int>().Result.Single().IsEqualTo(1);
multi.Read<int>().Single().IsEqualTo(2); multi.ReadAsync<int>().Result.Single().IsEqualTo(2);
} }
} }
} }
......
...@@ -31,5 +31,5 @@ ...@@ -31,5 +31,5 @@
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("1.29.0.0")] [assembly: AssemblyVersion("1.30.0.0")]
[assembly: AssemblyFileVersion("1.29.0.0")] [assembly: AssemblyFileVersion("1.30.0.0")]
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata schemaVersion="2"> <metadata schemaVersion="2">
<id>Dapper</id> <id>Dapper</id>
<version>1.29</version> <version>1.30</version>
<title>Dapper dot net</title> <title>Dapper dot net</title>
<authors>Sam Saffron,Marc Gravell</authors> <authors>Sam Saffron,Marc Gravell</authors>
<owners>Sam Saffron,Marc Gravell</owners> <owners>Sam Saffron,Marc Gravell</owners>
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
<frameworkAssembly assemblyName="Microsoft.CSharp" targetFramework=".NETFramework4.0-Client, .NETFramework4.0" /> <frameworkAssembly assemblyName="Microsoft.CSharp" targetFramework=".NETFramework4.0-Client, .NETFramework4.0" />
</frameworkAssemblies> </frameworkAssemblies>
<releaseNotes> <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.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.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 * 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