diff --git a/PetaPoco/Core/ColumnInfo.cs b/PetaPoco/Core/ColumnInfo.cs
index 8168691b..afa7d6f0 100644
--- a/PetaPoco/Core/ColumnInfo.cs
+++ b/PetaPoco/Core/ColumnInfo.cs
@@ -92,6 +92,18 @@ public class ColumnInfo
///
public string UpdateTemplate { get; set; }
+ ///
+ /// Creates and populates a ColumnInfo from the attributes of a POCO property.
+ ///
+ /// The POCO property to use for initializing the ColumnInfo.
+ /// A ColumnInfo instance.
+ public static ColumnInfo FromProperty(PropertyInfo propertyInfo)
+ {
+ var ci = new ColumnInfo();
+ PopulateFromProperty(propertyInfo, ref ci, out _);
+ return ci;
+ }
+
internal static void PopulateFromProperty(PropertyInfo pi, ref ColumnInfo ci, out ColumnAttribute columnAttr)
{
// Check if declaring poco has [Explicit] attribute
@@ -123,17 +135,5 @@ internal static void PopulateFromProperty(PropertyInfo pi, ref ColumnInfo ci, ou
}
}
}
-
- ///
- /// Creates and populates a ColumnInfo from the attributes of a POCO property.
- ///
- /// The POCO property to use for initializing the ColumnInfo.
- /// A ColumnInfo instance.
- public static ColumnInfo FromProperty(PropertyInfo propertyInfo)
- {
- var ci = new ColumnInfo();
- PopulateFromProperty(propertyInfo, ref ci, out _);
- return ci;
- }
}
}
diff --git a/PetaPoco/Core/IGridReader.cs b/PetaPoco/Core/IGridReader.cs
index a2406075..1681dec3 100644
--- a/PetaPoco/Core/IGridReader.cs
+++ b/PetaPoco/Core/IGridReader.cs
@@ -8,6 +8,8 @@ namespace PetaPoco
///
public interface IGridReader : IDisposable
{
+ #region ReadSinglePoco
+
///
/// Performs a read, returning the results as an collection.
///
@@ -15,6 +17,10 @@ public interface IGridReader : IDisposable
/// An enumerable collection of POCOs containing the result records.
IEnumerable Read();
+ #endregion
+
+ #region ReadMultiPoco : auto-mapping
+
///
IEnumerable Read();
@@ -31,6 +37,10 @@ public interface IGridReader : IDisposable
/// An enumerable collection of POCOs containing the result records.
IEnumerable Read();
+ #endregion
+
+ #region ReadMultiPoco : custom-mapping
+
///
IEnumerable Read(Func func);
@@ -48,5 +58,7 @@ public interface IGridReader : IDisposable
/// A callback function to used to connect the POCO instances, or to let PetaPoco automatically deduce the relationships.
/// An enumerable collection of POCOs containing the result records.
IEnumerable Read(Func func);
+
+ #endregion
}
}
diff --git a/PetaPoco/Core/PocoData.cs b/PetaPoco/Core/PocoData.cs
index 255f4ef0..a9e9ac31 100644
--- a/PetaPoco/Core/PocoData.cs
+++ b/PetaPoco/Core/PocoData.cs
@@ -74,6 +74,14 @@ public PocoData(Type type, IMapper defaultMapper)
QueryColumns = (from c in Columns where !c.Value.ResultColumn || c.Value.AutoSelectedResultColumn select c.Key).ToArray();
}
+ public static PocoData ForType(Type type, IMapper defaultMapper)
+ {
+ if (type == typeof(System.Dynamic.ExpandoObject))
+ throw new InvalidOperationException("Can't use dynamic types with this method");
+
+ return _pocoDatas.GetOrAdd(type, () => new PocoData(type, defaultMapper));
+ }
+
public static PocoData ForObject(object obj, string primaryKeyName, IMapper defaultMapper)
{
var t = obj.GetType();
@@ -97,20 +105,6 @@ public static PocoData ForObject(object obj, string primaryKeyName, IMapper defa
return ForType(t, defaultMapper);
}
- public static PocoData ForType(Type type, IMapper defaultMapper)
- {
- if (type == typeof(System.Dynamic.ExpandoObject))
- throw new InvalidOperationException("Can't use dynamic types with this method");
-
- return _pocoDatas.GetOrAdd(type, () => new PocoData(type, defaultMapper));
- }
-
- private static bool IsIntegralType(Type type)
- {
- var tc = Type.GetTypeCode(type);
- return tc >= TypeCode.SByte && tc <= TypeCode.UInt64;
- }
-
// Create factory function that can convert a IDataReader record into a POCO
public Delegate GetFactory(string sql, string connectionString, int firstColumn, int countColumns, IDataReader reader, IMapper defaultMapper)
{
@@ -393,6 +387,12 @@ private static Func GetConverter(IMapper mapper, PocoColumn pc,
return null;
}
+ private static bool IsIntegralType(Type type)
+ {
+ var tc = Type.GetTypeCode(type);
+ return tc >= TypeCode.SByte && tc <= TypeCode.UInt64;
+ }
+
private static T RecurseInheritedTypes(Type t, Func cb)
{
while (t != null)
diff --git a/PetaPoco/Database.cs b/PetaPoco/Database.cs
index e801890a..2d87c20e 100644
--- a/PetaPoco/Database.cs
+++ b/PetaPoco/Database.cs
@@ -18,46 +18,27 @@
namespace PetaPoco
{
///
- public class Database : IDatabase
+ public partial class Database : IDatabase
{
-#region Internal operations
-
- internal void DoPreExecute(IDbCommand cmd)
- {
- if (CommandTimeout > 0 || OneTimeCommandTimeout > 0)
- {
- cmd.CommandTimeout = OneTimeCommandTimeout > 0 ? OneTimeCommandTimeout : CommandTimeout;
- OneTimeCommandTimeout = 0;
- }
-
- _provider.PreExecute(cmd);
- OnExecutingCommand(cmd);
-
- _lastSql = cmd.CommandText;
- _lastArgs = cmd.Parameters.Cast().Select(parameter => parameter.Value).ToArray();
- }
-
-#endregion
-
-#region Member Fields
+ #region Member Fields
private IMapper _defaultMapper;
private string _connectionString;
private IProvider _provider;
+ private IsolationLevel? _isolationLevel;
private IDbConnection _sharedConnection;
private IDbTransaction _transaction;
+ private DbProviderFactory _factory;
private int _sharedConnectionDepth;
private int _transactionDepth;
private bool _transactionCancelled;
+ private string _paramPrefix;
private string _lastSql;
private object[] _lastArgs;
- private string _paramPrefix;
- private DbProviderFactory _factory;
- private IsolationLevel? _isolationLevel;
-#endregion
+ #endregion
-#region Constructors
+ #region Constructors
#if !NETSTANDARD
///
@@ -121,15 +102,6 @@ public Database(IDbConnection connection, IMapper defaultMapper = null)
Initialise(DatabaseProvider.Resolve(_sharedConnection.GetType(), false, _connectionString), defaultMapper);
}
- private void SetupFromConnection(IDbConnection connection)
- {
- _sharedConnection = connection;
- _connectionString = connection.ConnectionString;
-
- // Prevent closing external connection
- _sharedConnectionDepth = 2;
- }
-
///
/// Constructs an instance using a supplied connection string and provider name.
///
@@ -203,7 +175,7 @@ public Database(IDatabaseBuildConfiguration configuration)
if (configuration == null)
throw new ArgumentNullException(nameof(configuration));
- var settings = (IBuildConfigurationSettings) configuration;
+ var settings = (IBuildConfigurationSettings)configuration;
IMapper defaultMapper = null;
settings.TryGetSetting(DatabaseConfigurationExtensions.DefaultMapper, v => defaultMapper = v);
@@ -283,6 +255,15 @@ public Database(IDatabaseBuildConfiguration configuration)
settings.TryGetSetting>(DatabaseConfigurationExtensions.ExceptionThrown, v => ExceptionThrown += v);
}
+ private void SetupFromConnection(IDbConnection connection)
+ {
+ _sharedConnection = connection;
+ _connectionString = connection.ConnectionString;
+
+ // Prevent closing external connection
+ _sharedConnectionDepth = 2;
+ }
+
///
/// Provides common initialization for the various constructors.
///
@@ -301,15 +282,9 @@ private void Initialise(IProvider provider, IMapper mapper)
_defaultMapper = mapper ?? new ConventionMapper();
}
-#endregion
-
-#region Connection Management
+ #endregion
- ///
- /// When set to the first opened connection is kept alive until or is called.
- ///
- ///
- public bool KeepConnectionAlive { get; set; }
+ #region Connection Management (IConnection Implementation)
///
/// Provides access to the currently open shared connection.
@@ -430,14 +405,14 @@ public void Dispose()
CloseSharedConnection();
}
-#endregion
+ #endregion
-#region Transaction Management
+ #region Transaction Management (ITransactionAccessor, IDatabase implementation)
- ///
+ ///
IDbTransaction ITransactionAccessor.Transaction => _transaction;
- ///
+ ///
public ITransaction GetTransaction()
=> new Transaction(this);
@@ -458,7 +433,7 @@ public virtual void OnEndTransaction()
TransactionEnding?.Invoke(this, new DbTransactionEventArgs(_transaction));
}
- ///
+ ///
public void BeginTransaction()
{
_transactionDepth++;
@@ -473,11 +448,11 @@ public void BeginTransaction()
}
#if ASYNC
- ///
+ ///
public Task BeginTransactionAsync()
=> BeginTransactionAsync(CancellationToken.None);
- ///
+ ///
public async Task BeginTransactionAsync(CancellationToken cancellationToken)
{
if (_sharedConnection is DbConnection asyncConn)
@@ -506,6 +481,36 @@ public async Task BeginTransactionAsync(CancellationToken cancellationToken)
}
#endif
+ ///
+ public void AbortTransaction()
+ {
+ _transactionCancelled = true;
+ CompleteTransaction();
+ }
+
+#if ASYNC
+ public Task AbortTransactionAsync()
+ {
+ this._transactionCancelled = true;
+ return CompleteTransactionAsync();
+ }
+#endif
+
+ ///
+ public void CompleteTransaction()
+ {
+ if ((--_transactionDepth) == 0)
+ CleanupTransaction();
+ }
+
+#if ASYNC
+ public async Task CompleteTransactionAsync()
+ {
+ if ((--_transactionDepth) == 0)
+ await CleanupTransactionAsync().ConfigureAwait(false);
+ }
+#endif
+
///
/// Internal helper to cleanup transaction.
///
@@ -524,20 +529,6 @@ private void CleanupTransaction()
CloseSharedConnection();
}
- ///
- public void AbortTransaction()
- {
- _transactionCancelled = true;
- CompleteTransaction();
- }
-
- ///
- public void CompleteTransaction()
- {
- if ((--_transactionDepth) == 0)
- CleanupTransaction();
- }
-
#if ASYNC
#pragma warning disable 1998
private async Task CleanupTransactionAsync()
@@ -564,633 +555,946 @@ private async Task CleanupTransactionAsync()
}
}
#pragma warning restore 1998
+#endif
- public Task AbortTransactionAsync()
+ #endregion
+
+ #region Exception Reporting and Logging
+
+ ///
+ /// Called if an exception occurs during processing of a DB operation. Override to provide custom logging/handling.
+ ///
+ /// The exception instance.
+ /// to re-throw the exception, to suppress it.
+ public virtual bool OnException(Exception ex)
{
- this._transactionCancelled = true;
- return CompleteTransactionAsync();
+ System.Diagnostics.Debug.WriteLine(ex.ToString());
+ System.Diagnostics.Debug.WriteLine(LastCommand);
+
+ var args = new ExceptionEventArgs(ex);
+ ExceptionThrown?.Invoke(this, new ExceptionEventArgs(ex));
+ return args.Raise;
}
- public async Task CompleteTransactionAsync()
+ ///
+ /// Called when DB connection opened.
+ ///
+ ///
+ /// Override this method to provide custom logging of opened connections, or to provide a proxy IDbConnection.
+ ///
+ /// The newly-opened IDbConnection.
+ /// The same or a replacement IDbConnection.
+ public virtual IDbConnection OnConnectionOpened(IDbConnection conn)
{
- if ((--_transactionDepth) == 0)
- await CleanupTransactionAsync().ConfigureAwait(false);
+ var args = new DbConnectionEventArgs(conn);
+ ConnectionOpened?.Invoke(this, args);
+ return args.Connection;
}
-#endif
-#endregion
+ ///
+ /// Called before a DB connection is opened.
+ ///
+ ///
+ /// Override this method to provide custom logging of opening connections, or to provide a proxy IDbConnection.
+ ///
+ /// The soon-to-be-opened IDbConnection.
+ /// The same or a replacement IDbConnection.
+ public virtual IDbConnection OnConnectionOpening(IDbConnection conn)
+ {
+ var args = new DbConnectionEventArgs(conn);
+ ConnectionOpening?.Invoke(this, args);
+ return args.Connection;
+ }
-#region Command Management
+ ///
+ /// Called when DB connection closed.
+ ///
+ /// The soon-to-be-closed IDBConnection.
+ public virtual void OnConnectionClosing(IDbConnection conn)
+ {
+ ConnectionClosing?.Invoke(this, new DbConnectionEventArgs(conn));
+ }
///
- /// Adds a parameter to a DB command.
+ /// Called just before an DB command is executed.
///
- /// A reference to the IDbCommand to which the parameter is to be added.
- /// The value to assign to the parameter.
- /// Optional, a reference to the property info of the POCO property from which the value is coming.
- private void AddParam(IDbCommand cmd, object value, PocoColumn pc)
+ ///
+ /// Override this method to provide custom logging of commands, modification of the IDbCommand before it's executed, or any other custom actions that should be performed before every command
+ ///
+ /// The command to be executed.
+ public virtual void OnExecutingCommand(IDbCommand cmd)
{
- // Convert value to from poco type to db type
- if (pc?.PropertyInfo != null)
- {
- var mapper = Mappers.GetMapper(pc.PropertyInfo.DeclaringType, _defaultMapper);
- var fn = mapper.GetToDbConverter(pc.PropertyInfo);
- if (fn != null)
- value = fn(value);
- }
+ CommandExecuting?.Invoke(this, new DbCommandEventArgs(cmd));
+ }
- // Support passed in parameters
- if (value is IDbDataParameter idbParam)
- {
- if (cmd.CommandType == CommandType.Text)
- idbParam.ParameterName = cmd.Parameters.Count.EnsureParamPrefix(_paramPrefix);
- else if (idbParam.ParameterName?.StartsWith(_paramPrefix) != true)
- idbParam.ParameterName = idbParam.ParameterName.EnsureParamPrefix(_paramPrefix);
+ ///
+ /// Called on completion of command execution.
+ ///
+ ///
+ /// Override this method to provide custom logging or other actions after every command has completed.
+ ///
+ /// The IDbCommand that finished executing.
+ public virtual void OnExecutedCommand(IDbCommand cmd)
+ {
+ CommandExecuted?.Invoke(this, new DbCommandEventArgs(cmd));
+ }
- cmd.Parameters.Add(idbParam);
- }
- else
- {
- var p = cmd.CreateParameter();
- p.ParameterName = cmd.Parameters.Count.EnsureParamPrefix(_paramPrefix);
- SetParameterProperties(p, value, pc);
+ #endregion
- cmd.Parameters.Add(p);
- }
- }
+ #region ExecuteNonQuery, ExecuteAsync
- private void SetParameterProperties(IDbDataParameter p, object value, PocoColumn pc)
- {
- // Assign the parameter value
- if (value == null)
- {
- p.Value = DBNull.Value;
+ ///
+ public int Execute(Sql sql)
+ => Execute(sql.SQL, sql.Arguments);
- if (pc?.PropertyInfo.PropertyType.Name == "Byte[]")
- p.DbType = DbType.Binary;
- }
- else
- {
- // Give the database type first crack at converting to DB required type
- value = _provider.MapParameterValue(value);
+ ///
+ public int Execute(string sql, params object[] args)
+ => ExecuteInternal(CommandType.Text, sql, args);
- var t = value.GetType();
+#if ASYNC
+ public Task ExecuteAsync(Sql sql)
+ => ExecuteInternalAsync(CancellationToken.None, CommandType.Text, sql.SQL, sql.Arguments);
- if (t == typeof(string) && pc?.ForceToAnsiString == true)
- {
- t = typeof(AnsiString);
- value = value.ToAnsiString();
- }
- if (t == typeof(DateTime) && pc?.ForceToDateTime2 == true)
- {
- t = typeof(DateTime2);
- value = ((DateTime)value).ToDateTime2();
- }
+ public Task ExecuteAsync(string sql, params object[] args)
+ => ExecuteInternalAsync(CancellationToken.None, CommandType.Text, sql, args);
- if (t.IsEnum) // PostgreSQL .NET driver wont cast enum to int
+ public Task ExecuteAsync(CancellationToken cancellationToken, Sql sql)
+ => ExecuteInternalAsync(cancellationToken, CommandType.Text, sql.SQL, sql.Arguments);
+
+ public Task ExecuteAsync(CancellationToken cancellationToken, string sql, params object[] args)
+ => ExecuteInternalAsync(cancellationToken, CommandType.Text, sql, args);
+#endif
+
+ protected virtual int ExecuteInternal(CommandType commandType, string sql, params object[] args)
+ {
+ try
+ {
+ OpenSharedConnection();
+ try
{
- p.Value = Convert.ChangeType(value, ((Enum)value).GetTypeCode());
+ using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
+ {
+ return ExecuteNonQueryHelper(cmd);
+ }
}
- else if (t == typeof(Guid) && !_provider.HasNativeGuidSupport)
+ finally
{
- p.Value = value.ToString();
- p.DbType = DbType.String;
- p.Size = 40;
+ CloseSharedConnection();
}
- else if (t == typeof(string))
- {
- // out of memory exception occurs if trying to save more than 4000 characters to SQL Server CE NText column. Set before attempting to set Size, or Size will always max out at 4000
- if ((value as string).Length + 1 > 4000 && p.GetType().Name == "SqlCeParameter")
- p.GetType().GetProperty("SqlDbType").SetValue(p, SqlDbType.NText, null);
+ }
+ catch (Exception x)
+ {
+ if (OnException(x))
+ throw;
+ return -1;
+ }
+ }
- p.Size = Math.Max((value as string).Length + 1, 4000); // Help query plan caching by using common size
- p.Value = value;
- }
- else if (t == typeof(AnsiString))
+#if ASYNC
+ protected virtual async Task ExecuteInternalAsync(CancellationToken cancellationToken, CommandType commandType, string sql, params object[] args)
+ {
+ try
+ {
+ await OpenSharedConnectionAsync(cancellationToken).ConfigureAwait(false);
+ try
{
- var asValue = (value as AnsiString).Value;
- if (asValue == null)
- {
- p.Size = 0;
- p.Value = DBNull.Value;
- }
- else
+ using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
{
- p.Size = Math.Max(asValue.Length + 1, 4000);
- p.Value = asValue;
+ return await ExecuteNonQueryHelperAsync(cancellationToken, cmd).ConfigureAwait(false);
}
- // Thanks @DataChomp for pointing out the SQL Server indexing performance hit of using wrong string type on varchar
- p.DbType = DbType.AnsiString;
- }
- else if (t == typeof(DateTime2))
- {
- var dt2Value = (value as DateTime2)?.Value;
- p.Value = dt2Value ?? (object)DBNull.Value;
- p.DbType = DbType.DateTime2;
- }
- else if (value.GetType().Name == "SqlGeography") //SqlGeography is a CLR Type
- {
- p.GetType().GetProperty("UdtTypeName").SetValue(p, "geography", null); //geography is the equivalent SQL Server Type
- p.Value = value;
- }
- else if (value.GetType().Name == "SqlGeometry") //SqlGeometry is a CLR Type
- {
- p.GetType().GetProperty("UdtTypeName").SetValue(p, "geometry", null); //geography is the equivalent SQL Server Type
- p.Value = value;
- }
- else if (t == typeof(byte[]))
- {
- p.Value = value;
- p.DbType = DbType.Binary;
}
- else
+ finally
{
- p.Value = value;
+ CloseSharedConnection();
}
}
+ catch (Exception x)
+ {
+ if (OnException(x))
+ throw;
+ return -1;
+ }
}
+#endif
- public IDbCommand CreateCommand(IDbConnection connection, string sql, params object[] args)
- => CreateCommand(connection, CommandType.Text, sql, args);
+ #endregion
- public IDbCommand CreateCommand(IDbConnection connection, CommandType commandType, string sql, params object[] args)
- {
- var cmd = connection.CreateCommand();
+ #region ExecuteScalar, ExecuteScalarAsync
+
+ ///
+ public T ExecuteScalar(Sql sql)
+ => ExecuteScalar(sql.SQL, sql.Arguments);
+
+ ///
+ public T ExecuteScalar(string sql, params object[] args)
+ => ExecuteScalarInternal(CommandType.Text, sql, args);
+
+#if ASYNC
+ ///
+ public Task ExecuteScalarAsync(Sql sql)
+ => ExecuteScalarInternalAsync(CancellationToken.None, CommandType.Text, sql.SQL, sql.Arguments);
+
+ ///
+ public Task ExecuteScalarAsync(string sql, params object[] args)
+ => ExecuteScalarInternalAsync(CancellationToken.None, CommandType.Text, sql, args);
+
+ ///
+ public Task ExecuteScalarAsync(CancellationToken cancellationToken, Sql sql)
+ => ExecuteScalarInternalAsync(cancellationToken, CommandType.Text, sql.SQL, sql.Arguments);
+
+ ///
+ public Task ExecuteScalarAsync(CancellationToken cancellationToken, string sql, params object[] args)
+ => ExecuteScalarInternalAsync(cancellationToken, CommandType.Text, sql, args);
+#endif
+ protected virtual T ExecuteScalarInternal(CommandType commandType, string sql, params object[] args)
+ {
try
{
- cmd.CommandType = commandType;
- cmd.Transaction = _transaction;
-
- switch (commandType)
+ OpenSharedConnection();
+ try
{
- case CommandType.Text:
- // Perform named argument replacements
- if (EnableNamedParams)
- {
- var newArgs = new List();
- sql = ParametersHelper.ProcessQueryParams(sql, args, newArgs);
- args = newArgs.ToArray();
- }
+ using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
+ {
+ var val = ExecuteScalarHelper(cmd);
- // Perform parameter prefix replacements
- if (_paramPrefix != "@")
- sql = sql.ReplaceParamPrefix(_paramPrefix);
- sql = sql.Replace("@@", "@"); // <- double @@ escapes a single @
- break;
- case CommandType.StoredProcedure:
- args = ParametersHelper.ProcessStoredProcParams(cmd, args, SetParameterProperties);
- break;
- case CommandType.TableDirect:
- break;
+ // Handle nullable types
+ var u = Nullable.GetUnderlyingType(typeof(T));
+ if (u != null && (val == null || val == DBNull.Value))
+ return default(T);
+
+ return (T) Convert.ChangeType(val, u == null ? typeof(T) : u);
+ }
+ }
+ finally
+ {
+ CloseSharedConnection();
}
+ }
+ catch (Exception x)
+ {
+ if (OnException(x))
+ throw;
+ return default(T);
+ }
+ }
- cmd.CommandText = sql;
+#if ASYNC
+ protected virtual async Task ExecuteScalarInternalAsync(CancellationToken cancellationToken, CommandType commandType, string sql, params object[] args)
+ {
+ try
+ {
+ await OpenSharedConnectionAsync(cancellationToken).ConfigureAwait(false);
+ try
+ {
+ using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
+ {
+ var val = await ExecuteScalarHelperAsync(cancellationToken, cmd).ConfigureAwait(false);
- foreach (var item in args)
- AddParam(cmd, item, null);
+ var u = Nullable.GetUnderlyingType(typeof(T));
+ if (u != null && (val == null || val == DBNull.Value))
+ return default(T);
- return cmd;
+ return (T) Convert.ChangeType(val, u == null ? typeof(T) : u);
+ }
+ }
+ finally
+ {
+ CloseSharedConnection();
+ }
}
- catch
+ catch (Exception x)
{
- cmd.Dispose();
- throw;
+ if (OnException(x))
+ throw;
+ return default(T);
}
}
+#endif
- ///
- /// Creates an IDbDataParameter with default values.
- ///
- /// The IDbDataParameter.
- public IDbDataParameter CreateParameter() => _factory.CreateParameter();
+ #endregion
- ///
- /// Creates an IDbDataParameter with the given name and value.
- ///
- /// The parameter name.
- /// The parameter value.
- /// The IDbDataParameter.
- public IDbDataParameter CreateParameter(string name, object value)
- => CreateParameter(name, value, ParameterDirection.Input);
+ #region Query, QueryAsync : Single-Poco
- ///
- /// Creates an IDbDataParameter with the given name and direction.
- ///
- /// The parameter name.
- /// The parameter direction.
- /// The IDbDataParameter.
- public IDbDataParameter CreateParameter(string name, ParameterDirection direction)
- => CreateParameter(name, null, direction);
+ ///
+ public IEnumerable Query()
+ => Query(string.Empty);
- ///
- /// Create an IDbParameter with the given ParameterName, Value, and Direction.
- ///
- /// The parameter name.
- /// The parameter value.
- /// The parameter direction.
- /// The IDbDataParameter.
- public IDbDataParameter CreateParameter(string name, object value, ParameterDirection direction)
+ ///
+ public IEnumerable Query(Sql sql)
+ => Query(sql.SQL, sql.Arguments);
+
+ ///
+ public IEnumerable Query(string sql, params object[] args)
{
- var result = CreateParameter();
- result.ParameterName = name;
- result.Value = value;
- result.Direction = direction;
- return result;
+ if (EnableAutoSelect)
+ sql = AutoSelectHelper.AddSelectClause(_provider, sql, _defaultMapper);
+
+ return ExecuteReader(CommandType.Text, sql, args);
}
-#endregion
+#if ASYNC
+ ///
+ public Task> QueryAsync()
+ => QueryAsync(CancellationToken.None, CommandType.Text, string.Empty);
-#region Exception Reporting and Logging
+ ///
+ public Task> QueryAsync(Sql sql)
+ => QueryAsync(CancellationToken.None, CommandType.Text, sql.SQL, sql.Arguments);
- ///
- /// Called if an exception occurs during processing of a DB operation. Override to provide custom logging/handling.
- ///
- /// The exception instance.
- /// to re-throw the exception, to suppress it.
- public virtual bool OnException(Exception ex)
- {
- System.Diagnostics.Debug.WriteLine(ex.ToString());
- System.Diagnostics.Debug.WriteLine(LastCommand);
+ ///
+ public Task> QueryAsync(string sql, params object[] args)
+ => QueryAsync(CancellationToken.None, CommandType.Text, sql, args);
- var args = new ExceptionEventArgs(ex);
- ExceptionThrown?.Invoke(this, new ExceptionEventArgs(ex));
- return args.Raise;
- }
+ ///
+ public Task> QueryAsync(CancellationToken cancellationToken)
+ => QueryAsync(cancellationToken, CommandType.Text, string.Empty);
- ///
- /// Called when DB connection opened.
- ///
- ///
- /// Override this method to provide custom logging of opened connections, or to provide a proxy IDbConnection.
- ///
- /// The newly-opened IDbConnection.
- /// The same or a replacement IDbConnection.
- public virtual IDbConnection OnConnectionOpened(IDbConnection conn)
- {
- var args = new DbConnectionEventArgs(conn);
- ConnectionOpened?.Invoke(this, args);
- return args.Connection;
- }
+ ///
+ public Task> QueryAsync(CancellationToken cancellationToken, Sql sql)
+ => QueryAsync(cancellationToken, CommandType.Text, sql.SQL, sql.Arguments);
- ///
- /// Called before a DB connection is opened.
- ///
- ///
- /// Override this method to provide custom logging of opening connections, or to provide a proxy IDbConnection.
- ///
- /// The soon-to-be-opened IDbConnection.
- /// The same or a replacement IDbConnection.
- public virtual IDbConnection OnConnectionOpening(IDbConnection conn)
- {
- var args = new DbConnectionEventArgs(conn);
- ConnectionOpening?.Invoke(this, args);
- return args.Connection;
- }
+ ///
+ public Task> QueryAsync(CancellationToken cancellationToken, string sql, params object[] args)
+ => QueryAsync(CancellationToken.None, CommandType.Text, sql, args);
- ///
- /// Called when DB connection closed.
- ///
- /// The soon-to-be-closed IDBConnection.
- public virtual void OnConnectionClosing(IDbConnection conn)
- {
- ConnectionClosing?.Invoke(this, new DbConnectionEventArgs(conn));
- }
+ ///
+ public Task> QueryAsync(CommandType commandType)
+ => QueryAsync(CancellationToken.None, commandType, string.Empty);
- ///
- /// Called just before an DB command is executed.
- ///
- ///
- /// Override this method to provide custom logging of commands, modification of the IDbCommand before it's executed, or any other custom actions that should be performed before every command
- ///
- /// The command to be executed.
- public virtual void OnExecutingCommand(IDbCommand cmd)
- {
- CommandExecuting?.Invoke(this, new DbCommandEventArgs(cmd));
- }
+ ///
+ public Task> QueryAsync(CommandType commandType, Sql sql)
+ => QueryAsync(CancellationToken.None, commandType, sql.SQL, sql.Arguments);
- ///
- /// Called on completion of command execution.
- ///
- ///
- /// Override this method to provide custom logging or other actions after every command has completed.
- ///
- /// The IDbCommand that finished executing.
- public virtual void OnExecutedCommand(IDbCommand cmd)
+ ///
+ public Task> QueryAsync(CommandType commandType, string sql, params object[] args)
+ => QueryAsync(CancellationToken.None, commandType, sql, args);
+
+ ///
+ public Task> QueryAsync(CancellationToken cancellationToken, CommandType commandType)
+ => QueryAsync(cancellationToken, commandType, string.Empty);
+
+ ///
+ public Task> QueryAsync(CancellationToken cancellationToken, CommandType commandType, Sql sql)
+ => QueryAsync(cancellationToken, commandType, sql.SQL, sql.Arguments);
+
+ ///
+ public Task> QueryAsync(CancellationToken cancellationToken, CommandType commandType, string sql, params object[] args)
{
- CommandExecuted?.Invoke(this, new DbCommandEventArgs(cmd));
+ if (EnableAutoSelect)
+ sql = AutoSelectHelper.AddSelectClause(_provider, sql, _defaultMapper);
+
+ return ExecuteReaderAsync(cancellationToken, commandType, sql, args);
}
-#endregion
+ ///
+ public Task QueryAsync(Action receivePocoCallback)
+ => QueryAsync(receivePocoCallback, CancellationToken.None, CommandType.Text, string.Empty);
-#region operation: Execute
+ ///
+ public Task QueryAsync(Action receivePocoCallback, Sql sql)
+ => QueryAsync(receivePocoCallback, CancellationToken.None, CommandType.Text, sql.SQL, sql.Arguments);
- ///
- public int Execute(string sql, params object[] args)
- => ExecuteInternal(CommandType.Text, sql, args);
+ ///
+ public Task QueryAsync(Action receivePocoCallback, string sql, params object[] args)
+ => QueryAsync(receivePocoCallback, CancellationToken.None, CommandType.Text, sql, args);
- ///
- public int Execute(Sql sql)
- => Execute(sql.SQL, sql.Arguments);
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CancellationToken cancellationToken)
+ => QueryAsync(receivePocoCallback, cancellationToken, CommandType.Text, string.Empty);
- protected virtual int ExecuteInternal(CommandType commandType, string sql, params object[] args)
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CancellationToken cancellationToken, Sql sql)
+ => QueryAsync(receivePocoCallback, cancellationToken, CommandType.Text, sql.SQL, sql.Arguments);
+
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CancellationToken cancellationToken, string sql, params object[] args)
+ => QueryAsync(receivePocoCallback, CancellationToken.None, CommandType.Text, sql, args);
+
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CommandType commandType)
+ => QueryAsync(receivePocoCallback, CancellationToken.None, commandType, string.Empty);
+
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CommandType commandType, Sql sql)
+ => QueryAsync(receivePocoCallback, CancellationToken.None, commandType, sql.SQL, sql.Arguments);
+
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CommandType commandType, string sql, params object[] args)
+ => QueryAsync(receivePocoCallback, CancellationToken.None, commandType, sql, args);
+
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CancellationToken cancellationToken, CommandType commandType)
+ => QueryAsync(receivePocoCallback, cancellationToken, commandType, string.Empty);
+
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CancellationToken cancellationToken, CommandType commandType, Sql sql)
+ => QueryAsync(receivePocoCallback, cancellationToken, commandType, sql.SQL, sql.Arguments);
+
+ ///
+ public Task QueryAsync(Action receivePocoCallback, CancellationToken cancellationToken, CommandType commandType, string sql, params object[] args)
+ {
+ if (EnableAutoSelect)
+ sql = AutoSelectHelper.AddSelectClause(_provider, sql, _defaultMapper);
+ return ExecuteReaderAsync(receivePocoCallback, cancellationToken, commandType, sql, args);
+ }
+
+#endif
+
+ protected virtual IEnumerable ExecuteReader(CommandType commandType, string sql, params object[] args)
{
+ OpenSharedConnection();
try
{
- OpenSharedConnection();
- try
+ using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
{
- using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
+ IDataReader r;
+ var pd = PocoData.ForType(typeof(T), _defaultMapper);
+ try
{
- return ExecuteNonQueryHelper(cmd);
+ r = ExecuteReaderHelper(cmd);
+ }
+ catch (Exception x)
+ {
+ if (OnException(x))
+ throw;
+ yield break;
+ }
+
+ var factory = pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, 0, r.FieldCount, r,
+ _defaultMapper) as Func;
+ using (r)
+ {
+ while (true)
+ {
+ T poco;
+ try
+ {
+ if (!r.Read())
+ yield break;
+ poco = factory(r);
+ }
+ catch (Exception x)
+ {
+ if (OnException(x))
+ throw;
+ yield break;
+ }
+
+ yield return poco;
+ }
}
- }
- finally
- {
- CloseSharedConnection();
}
}
- catch (Exception x)
+ finally
{
- if (OnException(x))
- throw;
- return -1;
+ CloseSharedConnection();
}
}
#if ASYNC
-
- public Task ExecuteAsync(string sql, params object[] args)
- => ExecuteInternalAsync(CancellationToken.None, CommandType.Text, sql, args);
-
- public Task ExecuteAsync(CancellationToken cancellationToken, string sql, params object[] args)
- => ExecuteInternalAsync(cancellationToken, CommandType.Text, sql, args);
-
- public Task ExecuteAsync(Sql sql)
- => ExecuteInternalAsync(CancellationToken.None, CommandType.Text, sql.SQL, sql.Arguments);
-
- public Task ExecuteAsync(CancellationToken cancellationToken, Sql sql)
- => ExecuteInternalAsync(cancellationToken, CommandType.Text, sql.SQL, sql.Arguments);
-
- protected virtual async Task ExecuteInternalAsync(CancellationToken cancellationToken, CommandType commandType, string sql, params object[] args)
+ protected virtual async Task> ExecuteReaderAsync(CancellationToken cancellationToken, CommandType commandType, string sql, object[] args)
{
+ await OpenSharedConnectionAsync(cancellationToken).ConfigureAwait(false);
+ var cmd = CreateCommand(_sharedConnection, commandType, sql, args);
+ IDataReader reader = null;
+ var pd = PocoData.ForType(typeof(T), _defaultMapper);
+
try
{
- await OpenSharedConnectionAsync(cancellationToken).ConfigureAwait(false);
+ reader = await ExecuteReaderHelperAsync(cancellationToken, cmd).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ if (OnException(e))
+ throw;
try
{
- using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
- {
- return await ExecuteNonQueryHelperAsync(cancellationToken, cmd).ConfigureAwait(false);
- }
+ cmd?.Dispose();
+ reader?.Dispose();
}
- finally
+ catch
{
- CloseSharedConnection();
+ // ignored
}
- }
- catch (Exception x)
- {
- if (OnException(x))
- throw;
- return -1;
- }
- }
-
-#endif
-#endregion
-
-#region operation: ExecuteScalar
+ return AsyncReader.Empty();
+ }
- ///
- public T ExecuteScalar(string sql, params object[] args)
- => ExecuteScalarInternal(CommandType.Text, sql, args);
+ var factory =
+ pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, 0, reader.FieldCount, reader, _defaultMapper) as Func;
- ///
- public T ExecuteScalar(Sql sql)
- => ExecuteScalar(sql.SQL, sql.Arguments);
+ return new AsyncReader(this, cmd, reader, factory);
+ }
- protected virtual T ExecuteScalarInternal(CommandType commandType, string sql, params object[] args)
+ protected virtual async Task ExecuteReaderAsync(Action processPoco, CancellationToken cancellationToken, CommandType commandType, string sql, object[] args)
{
+ await OpenSharedConnectionAsync(cancellationToken).ConfigureAwait(false);
try
{
- OpenSharedConnection();
- try
+ using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
{
- using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
- {
- var val = ExecuteScalarHelper(cmd);
-
- // Handle nullable types
- var u = Nullable.GetUnderlyingType(typeof(T));
- if (u != null && (val == null || val == DBNull.Value))
- return default(T);
+ IDataReader reader;
+ var pd = PocoData.ForType(typeof(T), _defaultMapper);
- return (T) Convert.ChangeType(val, u == null ? typeof(T) : u);
+ try
+ {
+ reader = await ExecuteReaderHelperAsync(cancellationToken, cmd).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ if (OnException(e))
+ throw;
+ return;
+ }
+
+ var readerAsync = reader as DbDataReader;
+ var factory =
+ pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, 0, reader.FieldCount, reader,
+ _defaultMapper) as Func;
+
+ using (reader)
+ {
+ while (true)
+ {
+ T poco;
+ try
+ {
+ if (readerAsync != null)
+ {
+ if (!await readerAsync.ReadAsync(cancellationToken).ConfigureAwait(false))
+ return;
+ }
+ else
+ {
+ if (!reader.Read())
+ return;
+ }
+
+ poco = factory(reader);
+ processPoco(poco);
+ }
+ catch (Exception e)
+ {
+ if (OnException(e))
+ throw;
+ return;
+ }
+ }
}
- }
- finally
- {
- CloseSharedConnection();
}
}
- catch (Exception x)
+ finally
{
- if (OnException(x))
- throw;
- return default(T);
+ CloseSharedConnection();
}
}
+#endif
-#if ASYNC
+ #endregion
- ///
- public Task ExecuteScalarAsync(string sql, params object[] args)
- => ExecuteScalarInternalAsync(CancellationToken.None, CommandType.Text, sql, args);
+ #region Query : Multi-Poco
- ///
- public Task ExecuteScalarAsync(CancellationToken cancellationToken, string sql, params object[] args)
- => ExecuteScalarInternalAsync(cancellationToken, CommandType.Text, sql, args);
+ ///
+ public IEnumerable Query(Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2) }, null, sql.SQL, sql.Arguments);
- ///
- public Task ExecuteScalarAsync(Sql sql)
- => ExecuteScalarInternalAsync(CancellationToken.None, CommandType.Text, sql.SQL, sql.Arguments);
+ ///
+ public IEnumerable Query(Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3) }, null, sql.SQL, sql.Arguments);
- ///
- public Task ExecuteScalarAsync(CancellationToken cancellationToken, Sql sql)
- => ExecuteScalarInternalAsync(cancellationToken, CommandType.Text, sql.SQL, sql.Arguments);
+ public IEnumerable Query(Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql.SQL, sql.Arguments);
+
+ ///
+ public IEnumerable Query(Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, null, sql.SQL, sql.Arguments);
+
+ ///
+ public IEnumerable Query(string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2) }, null, sql, args);
+
+ ///
+ public IEnumerable Query(string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3) }, null, sql, args);
+
+ ///
+ public IEnumerable Query(string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql, args);
+
+ ///
+ public IEnumerable Query(string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, null, sql, args);
+
+ ///
+ public IEnumerable Query(Func cb, Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2) }, cb, sql.SQL, sql.Arguments);
+
+ ///
+ public IEnumerable Query(Func cb, Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3) }, cb, sql.SQL, sql.Arguments);
+
+ ///
+ public IEnumerable Query(Func cb, Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, sql.SQL, sql.Arguments);
+
+ ///
+ public IEnumerable Query(Func cb, Sql sql)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, cb, sql.SQL, sql.Arguments);
+
+ ///
+ public IEnumerable Query(Func cb, string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2) }, cb, sql, args);
+
+ ///
+ public IEnumerable Query(Func cb, string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3) }, cb, sql, args);
+
+ ///
+ public IEnumerable Query(Func cb, string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, sql, args);
+
+ public IEnumerable Query(Func cb, string sql, params object[] args)
+ => Query(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, cb, sql, args);
- protected virtual async Task ExecuteScalarInternalAsync(CancellationToken cancellationToken, CommandType commandType, string sql,
- params object[] args)
+ ///
+ public IEnumerable Query(Type[] types, object cb, string sql, params object[] args)
{
+ OpenSharedConnection();
try
{
- await OpenSharedConnectionAsync(cancellationToken).ConfigureAwait(false);
- try
+ using (var cmd = CreateCommand(_sharedConnection, sql, args))
{
- using (var cmd = CreateCommand(_sharedConnection, commandType, sql, args))
+ IDataReader r;
+ try
{
- var val = await ExecuteScalarHelperAsync(cancellationToken, cmd).ConfigureAwait(false);
+ r = ExecuteReaderHelper(cmd);
+ }
+ catch (Exception x)
+ {
+ if (OnException(x))
+ throw;
+ yield break;
+ }
- var u = Nullable.GetUnderlyingType(typeof(T));
- if (u != null && (val == null || val == DBNull.Value))
- return default(T);
+ var factory = MultiPocoFactory.GetFactory(types, _sharedConnection.ConnectionString, sql, r, _defaultMapper);
+ if (cb == null)
+ cb = MultiPocoFactory.GetAutoMapper(types.ToArray());
+ var bNeedTerminator = false;
+ using (r)
+ {
+ while (true)
+ {
+ TRet poco;
+ try
+ {
+ if (!r.Read())
+ break;
+ poco = factory(r, cb);
+ }
+ catch (Exception x)
+ {
+ if (OnException(x))
+ throw;
+ yield break;
+ }
- return (T) Convert.ChangeType(val, u == null ? typeof(T) : u);
+ if (poco != null)
+ yield return poco;
+ else
+ bNeedTerminator = true;
+ }
+
+ if (bNeedTerminator)
+ {
+ var poco = (TRet) (cb as Delegate).DynamicInvoke(new object[types.Length]);
+ if (poco != null)
+ yield return poco;
+ else
+ yield break;
+ }
}
}
- finally
- {
- CloseSharedConnection();
- }
+ }
+ finally
+ {
+ CloseSharedConnection();
+ }
+ }
+
+ #endregion
+
+ #region QueryMultiple : Multi-POCO Result Set IGridReader
+
+ public IGridReader QueryMultiple(Sql sql)
+ => QueryMultiple(sql.SQL, sql.Arguments);
+
+ public IGridReader QueryMultiple(string sql, params object[] args)
+ {
+ OpenSharedConnection();
+
+ GridReader result = null;
+
+ var cmd = CreateCommand(_sharedConnection, sql, args);
+
+ try
+ {
+ var reader = ExecuteReaderHelper(cmd);
+ result = new GridReader(this, cmd, reader, _defaultMapper);
}
catch (Exception x)
{
if (OnException(x))
throw;
- return default(T);
}
- }
-#endif
+ return result;
+ }
-#endregion
+ #endregion
-#region operation: Fetch
+ #region Fetch, FetchAsync : Single-Poco
- ///
+ ///
public List Fetch()
=> Fetch(string.Empty);
- ///
- public List Fetch(string sql, params object[] args)
- => Query(sql, args).ToList();
-
- ///
+ ///
public List Fetch(Sql sql)
=> Fetch(sql.SQL, sql.Arguments);
- ///
- public List Fetch(long page, long itemsPerPage)
- => Fetch(page, itemsPerPage, string.Empty);
-
- ///
- public List Fetch(long page, long itemsPerPage, string sql, params object[] args)
- => SkipTake((page - 1) * itemsPerPage, itemsPerPage, sql, args);
-
- ///
- public List Fetch(long page, long itemsPerPage, Sql sql)
- => SkipTake((page - 1) * itemsPerPage, itemsPerPage, sql.SQL, sql.Arguments);
+ ///
+ public List Fetch(string sql, params object[] args)
+ => Query(sql, args).ToList();
#if ASYNC
- ///
+ ///
public Task> FetchAsync()
=> FetchAsync(CancellationToken.None, CommandType.Text, string.Empty);
- ///
- public Task> FetchAsync(CommandType commandType)
- => FetchAsync(CancellationToken.None, CommandType.Text, string.Empty);
+ ///
+ public Task> FetchAsync(Sql sql)
+ => FetchAsync(CancellationToken.None, CommandType.Text, sql.SQL, sql.Arguments);
+
+ ///
+ public Task> FetchAsync(string sql, params object[] args)
+ => FetchAsync(CancellationToken.None, CommandType.Text, sql, args);
- ///
+ ///
public Task> FetchAsync(CancellationToken cancellationToken)
=> FetchAsync(cancellationToken, CommandType.Text, string.Empty);
- ///
- public Task> FetchAsync(CancellationToken cancellationToken, CommandType commandType)
- => FetchAsync(cancellationToken, commandType, string.Empty);
+ ///
+ public Task> FetchAsync(CancellationToken cancellationToken, Sql sql)
+ => FetchAsync(cancellationToken, CommandType.Text, sql.SQL, sql.Arguments);
- ///
- public Task> FetchAsync