Commit 92172188 authored by Marc Gravell's avatar Marc Gravell

Make CommandBehavior more configurable; default to...

Make CommandBehavior more configurable; default to SingleRow=allowed,SingleResult=disallowed - but expose options; this fixes issues like #563 automatically, and allows the performance aspect  of #554 to be fixed via a global setting
parent d03f826d
...@@ -218,7 +218,7 @@ public static Task<T> QuerySingleOrDefaultAsync<T>(this IDbConnection cnn, Comma ...@@ -218,7 +218,7 @@ public static Task<T> QuerySingleOrDefaultAsync<T>(this IDbConnection cnn, Comma
private static Task<DbDataReader> ExecuteReaderWithFlagsFallbackAsync(DbCommand cmd, bool wasClosed, CommandBehavior behavior, CancellationToken cancellationToken) private static Task<DbDataReader> ExecuteReaderWithFlagsFallbackAsync(DbCommand cmd, bool wasClosed, CommandBehavior behavior, CancellationToken cancellationToken)
{ {
var task = cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken); var task = cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken);
if (task.Status == TaskStatus.Faulted && DisableCommandBehaviorOptimizations(behavior, task.Exception.InnerException)) if (task.Status == TaskStatus.Faulted && Settings.DisableCommandBehaviorOptimizations(behavior, task.Exception.InnerException))
{ // we can retry; this time it will have different flags { // we can retry; this time it will have different flags
task = cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken); task = cmd.ExecuteReaderAsync(GetBehavior(wasClosed, behavior), cancellationToken);
} }
......
namespace Dapper using System;
using System.Data;
namespace Dapper
{ {
partial class SqlMapper partial class SqlMapper
{ {
...@@ -7,6 +10,45 @@ partial class SqlMapper ...@@ -7,6 +10,45 @@ partial class SqlMapper
/// </summary> /// </summary>
public static class Settings public static class Settings
{ {
// disable single result by default; prevents errors AFTER the select being detected properly
const CommandBehavior DefaultAllowedCommandBehaviors = ~CommandBehavior.SingleResult;
internal static CommandBehavior AllowedCommandBehaviors { get; private set; } = DefaultAllowedCommandBehaviors;
private static void SetAllowedCommandBehaviors(CommandBehavior behavior, bool enabled)
{
if (enabled) AllowedCommandBehaviors |= behavior;
else AllowedCommandBehaviors &= ~behavior;
}
/// <summary>
/// Gets or sets whether dapper should use the CommandBehavior.SingleResult optimization
/// </summary>
public static bool UseSingleResultOptimization
{
get { return (AllowedCommandBehaviors & CommandBehavior.SingleResult) != 0; }
set { SetAllowedCommandBehaviors(CommandBehavior.SingleResult, value); }
}
/// <summary>
/// Gets or sets whether dapper should use the CommandBehavior.SingleRow optimization
/// </summary>
public static bool UseSingleRowOptimization
{
get { return (AllowedCommandBehaviors & CommandBehavior.SingleRow) != 0; }
set { SetAllowedCommandBehaviors(CommandBehavior.SingleRow, value); }
}
internal static bool DisableCommandBehaviorOptimizations(CommandBehavior behavior, Exception ex)
{
if (AllowedCommandBehaviors == DefaultAllowedCommandBehaviors
&& (behavior & (CommandBehavior.SingleResult | CommandBehavior.SingleRow)) != 0)
{
if (ex.Message.Contains(nameof(CommandBehavior.SingleResult))
|| ex.Message.Contains(nameof(CommandBehavior.SingleRow)))
{ // some providers just just allow these, so: try again without them and stop issuing them
SetAllowedCommandBehaviors(CommandBehavior.SingleResult | CommandBehavior.SingleRow, false);
return true;
}
}
return false;
}
static Settings() static Settings()
{ {
SetDefaults(); SetDefaults();
......
...@@ -235,9 +235,7 @@ private static void ResetTypeHandlers(bool clone) ...@@ -235,9 +235,7 @@ private static void ResetTypeHandlers(bool clone)
#endif #endif
AddTypeHandlerImpl(typeof(XmlDocument), new XmlDocumentHandler(), clone); AddTypeHandlerImpl(typeof(XmlDocument), new XmlDocumentHandler(), clone);
AddTypeHandlerImpl(typeof(XDocument), new XDocumentHandler(), clone); AddTypeHandlerImpl(typeof(XDocument), new XDocumentHandler(), clone);
AddTypeHandlerImpl(typeof(XElement), new XElementHandler(), clone); AddTypeHandlerImpl(typeof(XElement), new XElementHandler(), clone);
allowedCommandBehaviors = DefaultAllowedCommandBehaviors;
} }
#if !COREFX #if !COREFX
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
...@@ -912,7 +910,7 @@ private static IDataReader ExecuteReaderWithFlagsFallback(IDbCommand cmd, bool w ...@@ -912,7 +910,7 @@ private static IDataReader ExecuteReaderWithFlagsFallback(IDbCommand cmd, bool w
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ // thanks, Sqlite! { // thanks, Sqlite!
if (DisableCommandBehaviorOptimizations(behavior, ex)) if (Settings.DisableCommandBehaviorOptimizations(behavior, ex))
{ {
// we can retry; this time it will have different flags // we can retry; this time it will have different flags
return cmd.ExecuteReader(GetBehavior(wasClosed, behavior)); return cmd.ExecuteReader(GetBehavior(wasClosed, behavior));
...@@ -1324,25 +1322,10 @@ public static IEnumerable<TReturn> Query<TReturn>(this IDbConnection cnn, string ...@@ -1324,25 +1322,10 @@ public static IEnumerable<TReturn> Query<TReturn>(this IDbConnection cnn, string
} }
} }
} }
const CommandBehavior DefaultAllowedCommandBehaviors = ~((CommandBehavior)0);
static CommandBehavior allowedCommandBehaviors = DefaultAllowedCommandBehaviors;
private static bool DisableCommandBehaviorOptimizations(CommandBehavior behavior, Exception ex)
{
if(allowedCommandBehaviors == DefaultAllowedCommandBehaviors
&& (behavior & (CommandBehavior.SingleResult | CommandBehavior.SingleRow)) != 0)
{
if (ex.Message.Contains(nameof(CommandBehavior.SingleResult))
|| ex.Message.Contains(nameof(CommandBehavior.SingleRow)))
{ // some providers just just allow these, so: try again without them and stop issuing them
allowedCommandBehaviors = ~(CommandBehavior.SingleResult | CommandBehavior.SingleRow);
return true;
}
}
return false;
}
private static CommandBehavior GetBehavior(bool close, CommandBehavior @default) private static CommandBehavior GetBehavior(bool close, CommandBehavior @default)
{ {
return (close ? (@default | CommandBehavior.CloseConnection) : @default) & allowedCommandBehaviors; return (close ? (@default | CommandBehavior.CloseConnection) : @default) & Settings.AllowedCommandBehaviors;
} }
static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn, IDataReader reader, Identity identity, bool finalize) static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn, IDataReader reader, Identity identity, bool finalize)
{ {
......
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