/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012, Grzegorz Russek (grzegorz.russek@gmail.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* See: http://opensource.org/licenses/bsd-license.php
*
* Supported preprocessor flags:
* * DYNAMORM_OMMIT_OLDSYNTAX - Remove dynamic table functionality
* * DYNAMORM_OMMIT_GENERICEXECUTION - Remove generic execution functionality
* * DYNAMORM_OMMIT_TRYPARSE - Remove TryParse helpers (also applies DYNAMORM_OMMIT_GENERICEXECUTION)
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Data.Common;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using DynamORM.Builders;
using DynamORM.Builders.Extensions;
using DynamORM.Builders.Implementation;
using DynamORM.Helpers;
using DynamORM.Helpers.Dynamics;
using DynamORM.Mapper;
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "This is a generated file which generates all the necessary support classes.")]
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1403:FileMayOnlyContainASingleNamespace", Justification = "This is a generated file which generates all the necessary support classes.")]
namespace DynamORM
{
/// Small utility class to manage single columns.
public class DynamicColumn
{
#region Enums
/// Order By Order.
public enum SortOrder
{
/// Ascending order.
Asc,
/// Descending order.
Desc
}
/// Dynamic query operators.
public enum CompareOperator
{
/// Equals operator (default).
Eq,
/// Not equal operator.
Not,
/// Like operator.
Like,
/// Not like operator.
NotLike,
/// In operator.
In,
/// Less than operator.
Lt,
/// Less or equal operator.
Lte,
/// Greater than operator.
Gt,
/// Greater or equal operator.
Gte,
/// Between two values.
Between,
}
#endregion Enums
#region Constructors
/// Initializes a new instance of the class.
public DynamicColumn()
{
}
/// Initializes a new instance of the class.
/// Constructor provided for easier object creation in queries.
/// Name of column to set.
public DynamicColumn(string columnName)
: this()
{
ColumnName = columnName;
}
/// Initializes a new instance of the class.
/// Constructor provided for easier object creation in queries.
/// Name of column to set.
/// Compare column to value(s) operator.
/// Parameter value(s).
public DynamicColumn(string columnName, CompareOperator oper, object value)
: this(columnName)
{
Operator = oper;
Value = value;
}
#endregion Constructors
#region Properties
/// Gets or sets column name.
public string ColumnName { get; set; }
/// Gets or sets column alias.
/// Select specific.
public string Alias { get; set; }
/// Gets or sets aggregate function used on column.
/// Select specific.
public string Aggregate { get; set; }
/// Gets or sets order direction.
public SortOrder Order { get; set; }
/// Gets or sets value for parameters.
public object Value { get; set; }
/// Gets or sets condition operator.
public CompareOperator Operator { get; set; }
/// Gets or sets a value indicating whether this condition will be treated as or condition.
public bool Or { get; set; }
/// Gets or sets a value indicating whether start new block in where statement.
public bool BeginBlock { get; set; }
/// Gets or sets a value indicating whether end existing block in where statement.
public bool EndBlock { get; set; }
/// Gets or sets a value indicating whether set parameters for null values.
public bool? VirtualColumn { get; set; }
/// Gets or sets schema representation of a column.
/// Workaround to providers issues which sometimes pass wrong
/// data o schema. For example decimal has precision of 255 in SQL
/// server.
public DynamicSchemaColumn? Schema { get; set; }
#endregion Properties
#region Query creation helpers
#region Operators
private DynamicColumn SetOperatorAndValue(CompareOperator c, object v)
{
Operator = c;
Value = v;
return this;
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn Eq(object value)
{
return SetOperatorAndValue(CompareOperator.Eq, value);
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn Not(object value)
{
return SetOperatorAndValue(CompareOperator.Not, value);
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn Like(object value)
{
return SetOperatorAndValue(CompareOperator.Like, value);
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn NotLike(object value)
{
return SetOperatorAndValue(CompareOperator.NotLike, value);
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn Greater(object value)
{
return SetOperatorAndValue(CompareOperator.Gt, value);
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn Less(object value)
{
return SetOperatorAndValue(CompareOperator.Lt, value);
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn GreaterOrEqual(object value)
{
return SetOperatorAndValue(CompareOperator.Gte, value);
}
/// Helper method setting
/// to and
/// to provided value .
/// Value of parameter to set.
/// Returns self.
public DynamicColumn LessOrEqual(object value)
{
return SetOperatorAndValue(CompareOperator.Lte, value);
}
/// Helper method setting
/// to and
/// to provided values.
/// Value of from parameter to set.
/// Value of to parameter to set.
/// Returns self.
public DynamicColumn Between(object from, object to)
{
return SetOperatorAndValue(CompareOperator.Between, new[] { from, to });
}
/// Helper method setting
/// to and
/// to provided values.
/// Values of parameters to set.
/// Returns self.
public DynamicColumn In(IEnumerable values)
{
return SetOperatorAndValue(CompareOperator.In, values);
}
/// Helper method setting
/// to and
/// to provided values.
/// Values of parameters to set.
/// Returns self.
public DynamicColumn In(params object[] values)
{
if (values.Length == 1 && (values[0].GetType().IsCollection() || values[0] is IEnumerable))
return SetOperatorAndValue(CompareOperator.In, values[0]);
return SetOperatorAndValue(CompareOperator.In, values);
}
#endregion Operators
#region Order
/// Helper method setting
/// to ..
/// Returns self.
public DynamicColumn Asc()
{
Order = SortOrder.Asc;
return this;
}
/// Helper method setting
/// to ..
/// Returns self.
public DynamicColumn Desc()
{
Order = SortOrder.Desc;
return this;
}
#endregion Order
#region Other
/// Helper method setting
///
/// to provided name .
/// Name to set.
/// Returns self.
public DynamicColumn SetName(string name)
{
ColumnName = name;
return this;
}
/// Helper method setting
///
/// to provided alias .
/// Alias to set.
/// Returns self.
public DynamicColumn SetAlias(string alias)
{
Alias = alias;
return this;
}
/// Helper method setting
///
/// to provided aggregate .
/// Aggregate to set.
/// Returns self.
public DynamicColumn SetAggregate(string aggregate)
{
Aggregate = aggregate;
return this;
}
/// Sets the begin block flag.
/// If set to true [begin].
/// Returns self.
public DynamicColumn SetBeginBlock(bool begin = true)
{
BeginBlock = begin;
return this;
}
/// Sets the end block flag.
/// If set to true [end].
/// Returns self.
public DynamicColumn SetEndBlock(bool end = true)
{
EndBlock = end;
return this;
}
/// Sets the or flag.
/// If set to true [or].
/// Returns self.
public DynamicColumn SetOr(bool or = true)
{
Or = or;
return this;
}
/// Sets the virtual column.
/// Set virtual column value.
/// Returns self.
public DynamicColumn SetVirtualColumn(bool? virt)
{
VirtualColumn = virt;
return this;
}
#endregion Other
#endregion Query creation helpers
#region Parsing
/// Parse column for select query.
/// Column format consist of Column Name , Alias and
/// Aggregate function in this order separated by ': '.
/// Column string.
/// Instance of .
public static DynamicColumn ParseSelectColumn(string column)
{
// Split column description
var parts = column.Split(':');
if (parts.Length > 0)
{
DynamicColumn ret = new DynamicColumn() { ColumnName = parts[0] };
if (parts.Length > 1)
ret.Alias = parts[1];
if (parts.Length > 2)
ret.Aggregate = parts[2];
return ret;
}
return null;
}
/// Parse column for order by in query.
/// Column format consist of Column Name and
/// Direction in this order separated by ': '.
/// Column string.
/// Instance of .
public static DynamicColumn ParseOrderByColumn(string column)
{
// Split column description
var parts = column.Split(':');
if (parts.Length > 0)
{
DynamicColumn ret = new DynamicColumn() { ColumnName = parts[0] };
if (parts.Length > 1)
ret.Order = parts[1].ToLower() == "d" || parts[1].ToLower() == "desc" ? SortOrder.Desc : SortOrder.Asc;
if (parts.Length > 2)
ret.Alias = parts[2];
return ret;
}
return null;
}
#endregion Parsing
#region ToSQL
internal string ToSQLSelectColumn(DynamicDatabase db)
{
StringBuilder sb = new StringBuilder();
ToSQLSelectColumn(db, sb);
return sb.ToString();
}
internal void ToSQLSelectColumn(DynamicDatabase db, StringBuilder sb)
{
string column = ColumnName == "*" ? "*" : ColumnName;
if (column != "*" &&
(column.IndexOf(db.LeftDecorator) == -1 || column.IndexOf(db.RightDecorator) == -1) &&
(column.IndexOf('(') == -1 || column.IndexOf(')') == -1))
column = db.DecorateName(column);
string alias = Alias;
if (!string.IsNullOrEmpty(Aggregate))
{
sb.AppendFormat("{0}({1})", Aggregate, column);
alias = string.IsNullOrEmpty(alias) ?
ColumnName == "*" ? Guid.NewGuid().ToString() : ColumnName :
alias;
}
else
sb.Append(column);
if (!string.IsNullOrEmpty(alias))
sb.AppendFormat(" AS {0}", alias);
}
internal string ToSQLGroupByColumn(DynamicDatabase db)
{
StringBuilder sb = new StringBuilder();
ToSQLGroupByColumn(db, sb);
return sb.ToString();
}
internal void ToSQLGroupByColumn(DynamicDatabase db, StringBuilder sb)
{
sb.Append(db.DecorateName(ColumnName));
}
internal string ToSQLOrderByColumn(DynamicDatabase db)
{
StringBuilder sb = new StringBuilder();
ToSQLOrderByColumn(db, sb);
return sb.ToString();
}
internal void ToSQLOrderByColumn(DynamicDatabase db, StringBuilder sb)
{
if (!string.IsNullOrEmpty(Alias))
sb.Append(Alias);
else
sb.Append(db.DecorateName(ColumnName));
sb.AppendFormat(" {0}", Order.ToString().ToUpper());
}
#endregion ToSQL
}
/// Helper class to easy manage command.
public class DynamicCommand : IDbCommand, IExtendedDisposable
{
private IDbCommand _command;
private int? _commandTimeout = null;
private DynamicConnection _con;
private DynamicDatabase _db;
////private long _poolStamp = 0;
/// Initializes a new instance of the class.
/// The connection.
/// The database manager.
internal DynamicCommand(DynamicConnection con, DynamicDatabase db)
{
IsDisposed = false;
_con = con;
_db = db;
lock (_db.SyncLock)
{
if (!_db.CommandsPool.ContainsKey(_con.Connection))
throw new InvalidOperationException("Can't create command using disposed connection.");
else
{
_command = _con.Connection.CreateCommand();
_db.CommandsPool[_con.Connection].Add(this);
}
}
}
/// Initializes a new instance of the class.
/// The database manager.
/// Used internally to create command without context.
internal DynamicCommand(DynamicDatabase db)
{
IsDisposed = false;
_db = db;
_command = db.Provider.CreateCommand();
}
/// Prepare command for execution.
/// Returns edited instance.
internal IDbCommand PrepareForExecution()
{
// TODO: Fix that
////if (_poolStamp < _db.PoolStamp)
{
_command.CommandTimeout = _commandTimeout ?? _db.CommandTimeout ?? _command.CommandTimeout;
if (_db.TransactionPool[_command.Connection].Count > 0)
_command.Transaction = _db.TransactionPool[_command.Connection].Peek();
else
_command.Transaction = null;
////_poolStamp = _db.PoolStamp;
}
if (_db.DumpCommands)
_db.DumpCommand(_command);
return _command;
}
#region IDbCommand Members
///
/// Attempts to cancels the execution of an .
///
public void Cancel()
{
_command.Cancel();
}
///
/// Gets or sets the text command to run against the data source.
///
/// The text command to execute. The default value is an empty string ("").
public string CommandText { get { return _command.CommandText; } set { _command.CommandText = value; } }
///
/// Gets or sets the wait time before terminating the attempt to execute a command and generating an error.
///
/// The time (in seconds) to wait for the command to execute. The default value is 30 seconds.
/// The property value assigned is less than 0.
public int CommandTimeout { get { return _commandTimeout ?? _command.CommandTimeout; } set { _commandTimeout = value; } }
/// Gets or sets how the property is interpreted.
public CommandType CommandType { get { return _command.CommandType; } set { _command.CommandType = value; } }
/// Gets or sets the
/// used by this instance of the .
/// The connection to the data source.
public IDbConnection Connection
{
get { return _con; }
set
{
_con = value as DynamicConnection;
if (_con != null)
{
////_poolStamp = 0;
_command.Connection = _con.Connection;
}
else if (value == null)
{
_command.Transaction = null;
_command.Connection = null;
}
else
throw new InvalidOperationException("Can't assign direct IDbConnection implementation. This property accepts only DynamORM implementation of IDbConnection.");
}
}
/// Creates a new instance of an
/// object.
/// An object.
public IDbDataParameter CreateParameter()
{
return _command.CreateParameter();
}
/// Executes an SQL statement against the Connection object of a
/// data provider, and returns the number of rows affected.
/// The number of rows affected.
public int ExecuteNonQuery()
{
try
{
return PrepareForExecution().ExecuteNonQuery();
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// Executes the
/// against the ,
/// and builds an using one
/// of the values.
/// One of the
/// values.
/// An object.
public IDataReader ExecuteReader(CommandBehavior behavior)
{
try
{
return PrepareForExecution().ExecuteReader(behavior);
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// Executes the
/// against the and
/// builds an .
/// An object.
public IDataReader ExecuteReader()
{
try
{
return PrepareForExecution().ExecuteReader();
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// Executes the query, and returns the first column of the
/// first row in the result set returned by the query. Extra columns or
/// rows are ignored.
/// The first column of the first row in the result set.
public object ExecuteScalar()
{
try
{
return PrepareForExecution().ExecuteScalar();
}
catch (Exception ex)
{
throw new DynamicQueryException(ex, this);
}
}
/// Gets the .
public IDataParameterCollection Parameters
{
get { return _command.Parameters; }
}
/// Creates a prepared (or compiled) version of the command on the data source.
public void Prepare()
{
try
{
_command.Prepare();
}
catch (Exception ex)
{
throw new DynamicQueryException("Error preparing command.", ex, this);
}
}
/// Gets or sets the transaction within which the Command
/// object of a data provider executes.
/// It's does nothing, transaction is peeked from transaction
/// pool of a connection. This is only a dummy.
public IDbTransaction Transaction { get { return null; } set { } }
/// Gets or sets how command results are applied to the
/// when used by the
/// method of a .
/// One of the values. The default is
/// Both unless the command is automatically generated. Then the default is None.
/// The value entered was not one of the
/// values.
public UpdateRowSource UpdatedRowSource { get { return _command.UpdatedRowSource; } set { _command.UpdatedRowSource = value; } }
#endregion IDbCommand Members
#region IExtendedDisposable Members
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
public void Dispose()
{
lock (_db.SyncLock)
{
if (_con != null)
{
var pool = _db.CommandsPool.TryGetValue(_con.Connection);
if (pool != null && pool.Contains(this))
pool.Remove(this);
}
IsDisposed = true;
_command.Parameters.Clear();
_command.Dispose();
_command = null;
}
}
/// Gets a value indicating whether this instance is disposed.
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
}
/// Connection wrapper.
/// This class is only connection holder, connection is managed by
/// instance.
public class DynamicConnection : IDbConnection, IExtendedDisposable
{
private DynamicDatabase _db;
private bool _singleTransaction;
/// Gets underlying connection.
internal IDbConnection Connection { get; private set; }
/// Initializes a new instance of the class.
/// Database connection manager.
/// Active connection.
/// Are we using single transaction mode? I so... act correctly.
internal DynamicConnection(DynamicDatabase db, IDbConnection con, bool singleTransaction)
{
IsDisposed = false;
_db = db;
Connection = con;
_singleTransaction = singleTransaction;
}
/// Begins a database transaction.
/// One of the values.
/// Custom parameter describing transaction options.
/// This action is invoked when transaction is disposed.
/// Returns representation.
internal DynamicTransaction BeginTransaction(IsolationLevel? il, object custom, Action disposed)
{
return new DynamicTransaction(_db, this, _singleTransaction, il, disposed, null);
}
#region IDbConnection Members
/// Creates and returns a Command object associated with the connection.
/// A Command object associated with the connection.
public IDbCommand CreateCommand()
{
return new DynamicCommand(this, _db);
}
/// Begins a database transaction.
/// Returns representation.
public IDbTransaction BeginTransaction()
{
return BeginTransaction(null, null, null);
}
/// Begins a database transaction with the specified
/// value.
/// One of the values.
/// Returns representation.
public IDbTransaction BeginTransaction(IsolationLevel il)
{
return BeginTransaction(il, null, null);
}
/// Begins a database transaction with the specified
/// value.
/// Custom parameter describing transaction options.
/// Returns representation.
public IDbTransaction BeginTransaction(object custom)
{
return BeginTransaction(null, custom, null);
}
/// Changes the current database for an open Connection object.
/// The name of the database to use in place of the current database.
/// This operation is not supported in DynamORM . and will throw .
/// Thrown always.
public void ChangeDatabase(string databaseName)
{
throw new NotSupportedException("This operation is not supported in DynamORM.");
}
/// Opens a database connection with the settings specified by
/// the ConnectionString property of the provider-specific
/// Connection object.
/// Does nothing. handles
/// opening connections.
public void Open()
{
}
/// Closes the connection to the database.
/// Does nothing. handles
/// closing connections. Only way to close it is to dispose connection.
/// It will close if this is multi connection configuration, otherwise
/// it will stay open until is not
/// disposed.
public void Close()
{
}
/// Gets or sets the string used to open a database.
/// Changing connection string operation is not supported in DynamORM .
/// and will throw .
/// Thrown always when set is attempted.
public string ConnectionString
{
get { return Connection.ConnectionString; }
set { throw new NotSupportedException("This operation is not supported in DynamORM."); }
}
/// Gets the time to wait while trying to establish a connection
/// before terminating the attempt and generating an error.
public int ConnectionTimeout
{
get { return Connection.ConnectionTimeout; }
}
/// Gets the name of the current database or the database
/// to be used after a connection is opened.
public string Database
{
get { return Connection.Database; }
}
/// Gets the current state of the connection.
public ConnectionState State
{
get { return Connection.State; }
}
#endregion IDbConnection Members
#region IExtendedDisposable Members
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
public void Dispose()
{
_db.Close(Connection);
IsDisposed = true;
}
/// Gets a value indicating whether this instance is disposed.
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
}
/// Dynamic database is a class responsible for managing database.
public class DynamicDatabase : IExtendedDisposable
{
#region Internal fields and properties
private DbProviderFactory _provider;
private string _connectionString;
private bool _singleConnection;
private bool _singleTransaction;
private string _leftDecorator = "\"";
private string _rightDecorator = "\"";
private bool _leftDecoratorIsInInvalidMembersChars = true;
private bool _rightDecoratorIsInInvalidMembersChars = true;
private string _parameterFormat = "@{0}";
private int? _commandTimeout = null;
private long _poolStamp = 0;
private DynamicConnection _tempConn = null;
/// Provides lock object for this database instance.
internal readonly object SyncLock = new object();
/// Gets or sets timestamp of last transaction pool or configuration change.
/// This property is used to allow commands to determine if
/// they need to update transaction object or not.
internal long PoolStamp
{
get
{
long r = 0;
lock (SyncLock)
r = _poolStamp;
return r;
}
set
{
lock (SyncLock)
_poolStamp = value;
}
}
/// Gets pool of connections and transactions.
internal Dictionary> TransactionPool { get; private set; }
/// Gets pool of connections and commands.
/// Pool should contain dynamic commands instead of native ones.
internal Dictionary> CommandsPool { get; private set; }
/// Gets schema columns cache.
internal Dictionary> Schema { get; private set; }
/// Gets active builders that weren't disposed.
internal List RemainingBuilders { get; private set; }
#if !DYNAMORM_OMMIT_OLDSYNTAX
/// Gets tables cache for this database instance.
internal Dictionary TablesCache { get; private set; }
#endif
#endregion Internal fields and properties
#region Properties and Constructors
/// Gets database options.
public DynamicDatabaseOptions Options { get; private set; }
/// Gets or sets command timeout.
public int? CommandTimeout { get { return _commandTimeout; } set { _commandTimeout = value; _poolStamp = DateTime.Now.Ticks; } }
/// Gets the database provider.
public DbProviderFactory Provider { get { return _provider; } }
/// Gets or sets a value indicating whether
/// dump commands to console or not.
public bool DumpCommands { get; set; }
/// Initializes a new instance of the class.
/// Database provider by name.
/// Connection string to provided database.
/// Connection options.
public DynamicDatabase(string provider, string connectionString, DynamicDatabaseOptions options)
: this(DbProviderFactories.GetFactory(provider), connectionString, options)
{
}
/// Initializes a new instance of the class.
/// Database provider.
/// Connection string to provided database.
/// Connection options.
public DynamicDatabase(DbProviderFactory provider, string connectionString, DynamicDatabaseOptions options)
{
IsDisposed = false;
_provider = provider;
InitCommon(connectionString, options);
}
/// Initializes a new instance of the class.
/// Active database connection.
/// Connection options. required.
public DynamicDatabase(IDbConnection connection, DynamicDatabaseOptions options)
{
IsDisposed = false;
InitCommon(connection.ConnectionString, options);
TransactionPool.Add(connection, new Stack());
if (!_singleConnection)
throw new InvalidOperationException("This constructor accepts only connections with DynamicDatabaseOptions.SingleConnection option.");
}
private void InitCommon(string connectionString, DynamicDatabaseOptions options)
{
_connectionString = connectionString;
Options = options;
_singleConnection = (options & DynamicDatabaseOptions.SingleConnection) == DynamicDatabaseOptions.SingleConnection;
_singleTransaction = (options & DynamicDatabaseOptions.SingleTransaction) == DynamicDatabaseOptions.SingleTransaction;
DumpCommands = (options & DynamicDatabaseOptions.DumpCommands) == DynamicDatabaseOptions.DumpCommands;
TransactionPool = new Dictionary>();
CommandsPool = new Dictionary>();
Schema = new Dictionary>();
RemainingBuilders = new List();
#if !DYNAMORM_OMMIT_OLDSYNTAX
TablesCache = new Dictionary();
#endif
}
#endregion Properties and Constructors
#region Table
#if !DYNAMORM_OMMIT_OLDSYNTAX
/// Gets dynamic table which is a simple ORM using dynamic objects.
/// The action with instance of as parameter.
/// Table name.
/// Override keys in schema.
/// Owner of the table.
public void Table(Action action, string table = "", string[] keys = null, string owner = "")
{
using (dynamic t = Table(table, keys, owner))
action(t);
}
/// Gets dynamic table which is a simple ORM using dynamic objects.
/// Type used to determine table name.
/// The action with instance of as parameter.
/// Override keys in schema.
public void Table(Action action, string[] keys = null)
{
using (dynamic t = Table(keys))
action(t);
}
/// Gets dynamic table which is a simple ORM using dynamic objects.
/// Table name.
/// Override keys in schema.
/// Owner of the table.
/// Instance of .
public dynamic Table(string table = "", string[] keys = null, string owner = "")
{
string key = string.Concat(
table == null ? string.Empty : table,
keys == null ? string.Empty : string.Join("_|_", keys));
DynamicTable dt = null;
lock (SyncLock)
dt = TablesCache.TryGetValue(key) ??
TablesCache.AddAndPassValue(key,
new DynamicTable(this, table, owner, keys));
return dt;
}
/// Gets dynamic table which is a simple ORM using dynamic objects.
/// Type used to determine table name.
/// Override keys in schema.
/// Instance of .
public dynamic Table(string[] keys = null)
{
Type table = typeof(T);
string key = string.Concat(
table.FullName,
keys == null ? string.Empty : string.Join("_|_", keys));
DynamicTable dt = null;
lock (SyncLock)
dt = TablesCache.TryGetValue(key) ??
TablesCache.AddAndPassValue(key,
new DynamicTable(this, table, keys));
return dt;
}
/// Removes cached table.
/// Disposed dynamic table.
internal void RemoveFromCache(DynamicTable dynamicTable)
{
foreach (var item in TablesCache.Where(kvp => kvp.Value == dynamicTable).ToList())
TablesCache.Remove(item.Key);
}
#endif
#endregion Table
/// Adds cached builder.
/// New dynamic builder.
internal void AddToCache(IDynamicQueryBuilder builder)
{
lock (SyncLock)
RemainingBuilders.Add(builder);
}
/// Removes cached builder.
/// Disposed dynamic builder.
internal void RemoveFromCache(IDynamicQueryBuilder builder)
{
lock (SyncLock)
RemainingBuilders.Remove(builder);
}
#region From/Insert/Update/Delete
///
/// Adds to the FROM clause the contents obtained by parsing the dynamic lambda expressions given. The supported
/// formats are:
/// - Resolve to a string: x => "owner.Table AS Alias", where the alias part is optional.
/// - Resolve to an expression: x => x.owner.Table.As( x.Alias ), where the alias part is optional.
/// - Generic expression: x => x( expression ).As( x.Alias ), where the alias part is mandatory. In this
/// case the alias is not annotated.
///
/// The specification.
/// This instance to permit chaining.
public virtual IDynamicSelectQueryBuilder From(Func fn)
{
return new DynamicSelectQueryBuilder(this).From(fn);
}
/// Adds to the FROM clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicSelectQueryBuilder From()
{
return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)));
}
/// Adds to the FROM clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicSelectQueryBuilder From(Type t)
{
return new DynamicSelectQueryBuilder(this).From(x => x(t));
}
///
/// Adds to the INSERT INTO clause the contents obtained by parsing the dynamic lambda expressions given. The supported
/// formats are:
/// - Resolve to a string: x => "owner.Table".
/// - Resolve to a type: x => typeof(SomeClass).
/// - Resolve to an expression: x => x.owner.Table.
/// - Generic expression: x => x( expression ). Expression can
/// be or .
///
/// The specification.
/// This instance to permit chaining.
public virtual IDynamicInsertQueryBuilder Insert(Func func)
{
return new DynamicInsertQueryBuilder(this).Table(func);
}
/// Adds to the INSERT INTO clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicInsertQueryBuilder Insert()
{
return new DynamicInsertQueryBuilder(this).Table(typeof(T));
}
/// Adds to the INSERT INTO clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicInsertQueryBuilder Insert(Type t)
{
return new DynamicInsertQueryBuilder(this).Table(t);
}
/// Bulk insert objects into database.
/// Type of objects to insert.
/// Enumerable containing instances of objects to insert.
/// Number of inserted rows.
public virtual int Insert(IEnumerable e) where T : class
{
int affected = 0;
var mapper = DynamicMapperCache.GetMapper(typeof(T));
if (mapper != null)
{
using (var con = Open())
using (var tra = con.BeginTransaction())
using (var cmd = con.CreateCommand())
{
try
{
var parameters = new Dictionary();
if (!string.IsNullOrEmpty(mapper.InsertCommandText))
{
cmd.CommandText = mapper.InsertCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.InsertCommandParameter != null)
.OrderBy(di => di.InsertCommandParameter.Ordinal))
{
var para = cmd.CreateParameter();
para.ParameterName = col.InsertCommandParameter.Name;
para.DbType = col.InsertCommandParameter.Type;
cmd.Parameters.Add(para);
parameters[para] = col;
}
}
else
PrepareBatchInsert(mapper, cmd, parameters);
foreach (var o in e)
{
foreach (var m in parameters)
m.Key.Value = m.Value.Get(o);
affected += cmd.ExecuteNonQuery();
}
tra.Commit();
}
catch (Exception ex)
{
if (tra != null)
tra.Rollback();
affected = 0;
var problematicCommand = new StringBuilder();
cmd.Dump(problematicCommand);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
return affected;
}
///
/// Adds to the UPDATE clause the contents obtained by parsing the dynamic lambda expressions given. The supported
/// formats are:
/// - Resolve to a string: x => "owner.Table".
/// - Resolve to a type: x => typeof(SomeClass).
/// - Resolve to an expression: x => x.owner.Table.
/// - Generic expression: x => x( expression ). Expression can
/// be or .
///
/// The specification.
/// This instance to permit chaining.
public virtual IDynamicUpdateQueryBuilder Update(Func func)
{
return new DynamicUpdateQueryBuilder(this).Table(func);
}
/// Adds to the UPDATE clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicUpdateQueryBuilder Update()
{
return new DynamicUpdateQueryBuilder(this).Table(typeof(T));
}
/// Adds to the UPDATE clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicUpdateQueryBuilder Update(Type t)
{
return new DynamicUpdateQueryBuilder(this).Table(t);
}
/// Bulk update objects in database.
/// Type of objects to update.
/// Enumerable containing instances of objects to update.
/// Number of updated rows.
public virtual int Update(IEnumerable e) where T : class
{
int affected = 0;
var mapper = DynamicMapperCache.GetMapper(typeof(T));
if (mapper != null)
{
using (var con = Open())
using (var tra = con.BeginTransaction())
using (var cmd = con.CreateCommand())
{
try
{
var parameters = new Dictionary();
if (!string.IsNullOrEmpty(mapper.UpdateCommandText))
{
cmd.CommandText = mapper.UpdateCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.UpdateCommandParameter != null)
.OrderBy(di => di.UpdateCommandParameter.Ordinal))
{
var para = cmd.CreateParameter();
para.ParameterName = col.UpdateCommandParameter.Name;
para.DbType = col.UpdateCommandParameter.Type;
cmd.Parameters.Add(para);
parameters[para] = col;
}
}
else
PrepareBatchUpdate(mapper, cmd, parameters);
foreach (var o in e)
{
foreach (var m in parameters)
m.Key.Value = m.Value.Get(o);
affected += cmd.ExecuteNonQuery();
}
tra.Commit();
}
catch (Exception ex)
{
if (tra != null)
tra.Rollback();
affected = 0;
var problematicCommand = new StringBuilder();
cmd.Dump(problematicCommand);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
return affected;
}
/// Bulk update or insert objects into database.
/// Type of objects to update or insert.
/// Enumerable containing instances of objects to update or insert.
/// Number of updated or inserted rows.
public virtual int UpdateOrInsert(IEnumerable e) where T : class
{
int affected = 0;
var mapper = DynamicMapperCache.GetMapper(typeof(T));
if (mapper != null)
{
using (var con = Open())
using (var tra = con.BeginTransaction())
using (var cmdUp = con.CreateCommand())
using (var cmdIn = con.CreateCommand())
{
try
{
#region Update
var parametersUp = new Dictionary();
if (!string.IsNullOrEmpty(mapper.UpdateCommandText))
{
cmdUp.CommandText = mapper.UpdateCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.UpdateCommandParameter != null)
.OrderBy(di => di.UpdateCommandParameter.Ordinal))
{
var para = cmdUp.CreateParameter();
para.ParameterName = col.UpdateCommandParameter.Name;
para.DbType = col.UpdateCommandParameter.Type;
cmdUp.Parameters.Add(para);
parametersUp[para] = col;
}
}
else
PrepareBatchUpdate(mapper, cmdUp, parametersUp);
#endregion Update
#region Insert
var parametersIn = new Dictionary();
if (!string.IsNullOrEmpty(mapper.InsertCommandText))
{
cmdIn.CommandText = mapper.InsertCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.InsertCommandParameter != null)
.OrderBy(di => di.InsertCommandParameter.Ordinal))
{
var para = cmdIn.CreateParameter();
para.ParameterName = col.InsertCommandParameter.Name;
para.DbType = col.InsertCommandParameter.Type;
cmdIn.Parameters.Add(para);
parametersIn[para] = col;
}
}
else
PrepareBatchInsert(mapper, cmdIn, parametersIn);
#endregion Insert
foreach (var o in e)
{
foreach (var m in parametersUp)
m.Key.Value = m.Value.Get(o);
int a = cmdUp.ExecuteNonQuery();
if (a == 0)
{
foreach (var m in parametersIn)
m.Key.Value = m.Value.Get(o);
a = cmdIn.ExecuteNonQuery();
}
affected += a;
}
tra.Commit();
}
catch (Exception ex)
{
if (tra != null)
tra.Rollback();
affected = 0;
var problematicCommand = new StringBuilder();
cmdUp.Dump(problematicCommand);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
return affected;
}
///
/// Adds to the DELETE FROM clause the contents obtained by parsing the dynamic lambda expressions given. The supported
/// formats are:
/// - Resolve to a string: x => "owner.Table".
/// - Resolve to a type: x => typeof(SomeClass).
/// - Resolve to an expression: x => x.owner.Table.
/// - Generic expression: x => x( expression ). Expression can
/// be or .
///
/// The specification.
/// This instance to permit chaining.
public virtual IDynamicDeleteQueryBuilder Delete(Func func)
{
return new DynamicDeleteQueryBuilder(this).Table(func);
}
/// Adds to the DELETE FROM clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicDeleteQueryBuilder Delete()
{
return new DynamicDeleteQueryBuilder(this).Table(typeof(T));
}
/// Bulk delete objects in database.
/// Type of objects to delete.
/// Enumerable containing instances of objects to delete.
/// Number of deleted rows.
public virtual int Delete(IEnumerable e) where T : class
{
int affected = 0;
var mapper = DynamicMapperCache.GetMapper(typeof(T));
if (mapper != null)
{
using (var con = Open())
using (var tra = con.BeginTransaction())
using (var cmd = con.CreateCommand())
{
try
{
var parameters = new Dictionary();
if (!string.IsNullOrEmpty(mapper.DeleteCommandText))
{
cmd.CommandText = mapper.DeleteCommandText;
foreach (var col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.DeleteCommandParameter != null)
.OrderBy(di => di.DeleteCommandParameter.Ordinal))
{
var para = cmd.CreateParameter();
para.ParameterName = col.DeleteCommandParameter.Name;
para.DbType = col.DeleteCommandParameter.Type;
cmd.Parameters.Add(para);
parameters[para] = col;
}
}
else
PrepareBatchDelete(mapper, cmd, parameters);
foreach (var o in e)
{
foreach (var m in parameters)
m.Key.Value = m.Value.Get(o);
affected += cmd.ExecuteNonQuery();
}
tra.Commit();
}
catch (Exception ex)
{
if (tra != null)
tra.Rollback();
affected = 0;
var problematicCommand = new StringBuilder();
cmd.Dump(problematicCommand);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
return affected;
}
private void PrepareBatchInsert(DynamicTypeMap mapper, IDbCommand cmd, Dictionary parameters)
{
DynamicPropertyInvoker currentprop = null;
var temp = new Dictionary();
var schema = this.GetSchema();
int ord = 0;
var ib = Insert()
.SetVirtualMode(true)
.CreateTemporaryParameterAction(p => temp[p.Name] = currentprop)
.CreateParameterAction((p, cp) =>
{
parameters[cp] = temp[p.Name];
parameters[cp].InsertCommandParameter = new DynamicPropertyInvoker.ParameterSpec
{
Name = cp.ParameterName,
Type = cp.DbType,
Ordinal = ord++,
};
});
foreach (var prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore || (currentprop.Column != null && currentprop.Column.IsNoInsert))
continue;
if (currentprop.Get != null)
ib.Insert(new DynamicColumn()
{
ColumnName = col,
Schema = schema == null ? null : schema.TryGetNullable(col.ToLower()),
Operator = DynamicColumn.CompareOperator.Eq,
Value = null,
VirtualColumn = true,
});
}
ib.FillCommand(cmd);
// Cache command
mapper.InsertCommandText = cmd.CommandText;
}
private void PrepareBatchUpdate(DynamicTypeMap mapper, IDbCommand cmd, Dictionary parameters)
{
DynamicPropertyInvoker currentprop = null;
var temp = new Dictionary();
var schema = this.GetSchema();
int ord = 0;
var ib = Update()
.SetVirtualMode(true)
.CreateTemporaryParameterAction(p => temp[p.Name] = currentprop)
.CreateParameterAction((p, cp) =>
{
parameters[cp] = temp[p.Name];
parameters[cp].UpdateCommandParameter = new DynamicPropertyInvoker.ParameterSpec
{
Name = cp.ParameterName,
Type = cp.DbType,
Ordinal = ord++,
};
});
foreach (var prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore)
continue;
if (currentprop.Get != null)
{
var colS = schema == null ? null : schema.TryGetNullable(col.ToLower());
if (colS.HasValue)
{
if (colS.Value.IsKey)
ib.Where(new DynamicColumn()
{
ColumnName = col,
Schema = colS,
Operator = DynamicColumn.CompareOperator.Eq,
Value = null,
VirtualColumn = true,
});
else if (currentprop.Column == null || !currentprop.Column.IsNoUpdate)
ib.Values(new DynamicColumn()
{
ColumnName = col,
Schema = colS,
Operator = DynamicColumn.CompareOperator.Eq,
Value = null,
VirtualColumn = true,
});
}
else if (currentprop.Column != null && currentprop.Column.IsKey)
ib.Where(new DynamicColumn()
{
ColumnName = col,
Schema = colS,
Operator = DynamicColumn.CompareOperator.Eq,
Value = null,
VirtualColumn = true,
});
}
}
ib.FillCommand(cmd);
// Cache command
mapper.UpdateCommandText = cmd.CommandText;
}
private void PrepareBatchDelete(DynamicTypeMap mapper, IDbCommand cmd, Dictionary parameters)
{
DynamicPropertyInvoker currentprop = null;
var temp = new Dictionary();
var schema = this.GetSchema();
int ord = 0;
var ib = Delete()
.SetVirtualMode(true)
.CreateTemporaryParameterAction(p => temp[p.Name] = currentprop)
.CreateParameterAction((p, cp) =>
{
parameters[cp] = temp[p.Name];
parameters[cp].DeleteCommandParameter = new DynamicPropertyInvoker.ParameterSpec
{
Name = cp.ParameterName,
Type = cp.DbType,
Ordinal = ord++,
};
});
foreach (var prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
var col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore)
continue;
if (currentprop.Get != null)
{
var colS = schema == null ? null : schema.TryGetNullable(col.ToLower());
if (colS != null)
{
if (colS.Value.IsKey)
ib.Where(new DynamicColumn()
{
ColumnName = col,
Schema = colS,
Operator = DynamicColumn.CompareOperator.Eq,
Value = null,
VirtualColumn = true,
});
}
else if (currentprop.Column != null && currentprop.Column.IsKey)
ib.Where(new DynamicColumn()
{
ColumnName = col,
Schema = colS,
Operator = DynamicColumn.CompareOperator.Eq,
Value = null,
VirtualColumn = true,
});
}
}
ib.FillCommand(cmd);
// Cache command
mapper.DeleteCommandText = cmd.CommandText;
}
#endregion From/Insert/Update/Delete
#region Procedure
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Number of affected rows.
public virtual int Procedure(string procName)
{
return Procedure(procName, (DynamicExpando)null);
}
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Arguments (parameters) in form of expando object.
/// Number of affected rows.
public virtual int Procedure(string procName, params object[] args)
{
if ((Options & DynamicDatabaseOptions.SupportStoredProcedures) != DynamicDatabaseOptions.SupportStoredProcedures)
throw new InvalidOperationException("Database connection desn't support stored procedures.");
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(CommandType.StoredProcedure, procName)
.AddParameters(this, args)
.ExecuteNonQuery();
}
}
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Arguments (parameters) in form of expando object.
/// Number of affected rows.
public virtual int Procedure(string procName, DynamicExpando args)
{
if ((Options & DynamicDatabaseOptions.SupportStoredProcedures) != DynamicDatabaseOptions.SupportStoredProcedures)
throw new InvalidOperationException("Database connection desn't support stored procedures.");
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(CommandType.StoredProcedure, procName)
.AddParameters(this, args)
.ExecuteNonQuery();
}
}
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Arguments (parameters) in form of expando object.
/// Number of affected rows.
public virtual int Procedure(string procName, ExpandoObject args)
{
if ((Options & DynamicDatabaseOptions.SupportStoredProcedures) != DynamicDatabaseOptions.SupportStoredProcedures)
throw new InvalidOperationException("Database connection desn't support stored procedures.");
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(CommandType.StoredProcedure, procName)
.AddParameters(this, args)
.ExecuteNonQuery();
}
}
#endregion Procedure
#region Execute
/// Execute non query.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Number of affected rows.
public virtual int Execute(string sql, params object[] args)
{
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(sql).AddParameters(this, args)
.ExecuteNonQuery();
}
}
/// Execute non query.
/// Command builder.
/// Number of affected rows.
public virtual int Execute(IDynamicQueryBuilder builder)
{
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(builder)
.ExecuteNonQuery();
}
}
/// Execute non query.
/// Command builders.
/// Number of affected rows.
public virtual int Execute(IDynamicQueryBuilder[] builders)
{
int ret = 0;
using (var con = Open())
{
using (var trans = con.BeginTransaction())
{
foreach (var builder in builders)
{
using (var cmd = con.CreateCommand())
{
ret += cmd
.SetCommand(builder)
.ExecuteNonQuery();
}
}
trans.Commit();
}
}
return ret;
}
#endregion Execute
#region Scalar
/// Returns a single result.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Result of a query.
public virtual object Scalar(string sql, params object[] args)
{
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(sql).AddParameters(this, args)
.ExecuteScalar();
}
}
/// Returns a single result.
/// Command builder.
/// Result of a query.
public virtual object Scalar(IDynamicQueryBuilder builder)
{
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(builder)
.ExecuteScalar();
}
}
#if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE
/// Returns a single result.
/// What kind of result is expected.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Result of a query.
public virtual T ScalarAs(string sql, params object[] args)
{
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(sql).AddParameters(this, args)
.ExecuteScalarAs();
}
}
/// Returns a single result.
/// What kind of result is expected.
/// Command builder.
/// Default value.
/// Result of a query.
public virtual T ScalarAs(IDynamicQueryBuilder builder, T defaultValue = default(T))
{
using (var con = Open())
using (var cmd = con.CreateCommand())
{
return cmd
.SetCommand(builder)
.ExecuteScalarAs(defaultValue);
}
}
#endif
#endregion Scalar
#region Query
/// Enumerate the reader and yield the result.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Enumerator of objects expanded from query.
public virtual IEnumerable Query(string sql, params object[] args)
{
using (var con = Open())
using (var cmd = con.CreateCommand())
using (var rdr = cmd
.SetCommand(sql)
.AddParameters(this, args)
.ExecuteReader())
while (rdr.Read())
{
dynamic val = null;
// Work around to avoid yield being in try...catch block:
// http://stackoverflow.com/questions/346365/why-cant-yield-return-appear-inside-a-try-block-with-a-catch
try
{
val = rdr.RowToDynamic();
}
catch (ArgumentException argex)
{
var sb = new StringBuilder();
cmd.Dump(sb);
throw new ArgumentException(string.Format("{0}{1}{2}", argex.Message, Environment.NewLine, sb),
argex.InnerException.NullOr(a => a, argex));
}
yield return val;
}
}
/// Enumerate the reader and yield the result.
/// Command builder.
/// Enumerator of objects expanded from query.
public virtual IEnumerable Query(IDynamicQueryBuilder builder)
{
using (var con = Open())
using (var cmd = con.CreateCommand())
using (var rdr = cmd
.SetCommand(builder)
.ExecuteReader())
while (rdr.Read())
{
dynamic val = null;
// Work around to avoid yield being in try...catch block:
// http://stackoverflow.com/questions/346365/why-cant-yield-return-appear-inside-a-try-block-with-a-catch
try
{
val = rdr.RowToDynamic();
}
catch (ArgumentException argex)
{
var sb = new StringBuilder();
cmd.Dump(sb);
throw new ArgumentException(string.Format("{0}{1}{2}", argex.Message, Environment.NewLine, sb),
argex.InnerException.NullOr(a => a, argex));
}
yield return val;
}
}
#endregion Query
#region Schema
/// Builds query cache if necessary and returns it.
/// The builder containing query to read schema from.
/// Query schema.
public Dictionary GetQuerySchema(IDynamicSelectQueryBuilder builder)
{
using (var con = Open())
using (var cmd = con.CreateCommand().SetCommand(builder))
return ReadSchema(cmd)
.Distinct()
.ToDictionary(k => k.Name.ToLower(), k => k);
}
/// Builds query cache if necessary and returns it.
/// SQL query from which read schema.
/// SQL query arguments.
/// Query schema.
public Dictionary GetQuerySchema(string sql, params object[] args)
{
using (var con = Open())
using (var cmd = con.CreateCommand().SetCommand(sql, args))
return ReadSchema(cmd)
.Distinct()
.ToDictionary(k => k.Name.ToLower(), k => k);
}
/// Builds table cache if necessary and returns it.
/// Name of table for which build schema.
/// Owner of table for which build schema.
/// Table schema.
public Dictionary GetSchema(string table, string owner = null)
{
Dictionary schema = null;
lock (SyncLock)
schema = Schema.TryGetValue(table.ToLower()) ??
BuildAndCacheSchema(table, null, owner);
return schema;
}
/// Builds table cache if necessary and returns it.
/// Type of table for which build schema.
/// Table schema or null if type was anonymous.
public Dictionary GetSchema()
{
if (typeof(T).IsAnonymous())
return null;
Dictionary schema = null;
lock (SyncLock)
schema = Schema.TryGetValue(typeof(T).FullName) ??
BuildAndCacheSchema(null, DynamicMapperCache.GetMapper());
return schema;
}
/// Builds table cache if necessary and returns it.
/// Type of table for which build schema.
/// Table schema or null if type was anonymous.
public Dictionary GetSchema(Type table)
{
if (table == null || table.IsAnonymous() || table.IsValueType)
return null;
Dictionary schema = null;
lock (SyncLock)
schema = Schema.TryGetValue(table.FullName) ??
BuildAndCacheSchema(null, DynamicMapperCache.GetMapper(table));
return schema;
}
/// Clears the schema from cache.
/// Use this method to refresh table information.
/// Name of table for which clear schema.
/// Owner of table for which clear schema.
public void ClearSchema(string table = null, string owner = null)
{
lock (SyncLock)
if (Schema.ContainsKey(table.ToLower()))
Schema.Remove(table.ToLower());
}
/// Clears the schema from cache.
/// Use this method to refresh table information.
/// Type of table for which clear schema.
public void ClearSchema()
{
ClearSchema(typeof(T));
}
/// Clears the schema from cache.
/// Use this method to refresh table information.
/// Type of table for which clear schema.
public void ClearSchema(Type table)
{
lock (SyncLock)
if (Schema.ContainsKey(table.FullName))
{
if (Schema[table.FullName] != null)
Schema[table.FullName].Clear();
Schema.Remove(table.FullName);
}
}
/// Clears the all schemas from cache.
/// Use this method to refresh all table information.
public void ClearSchema()
{
lock (SyncLock)
{
foreach (var s in Schema)
if (s.Value != null)
s.Value.Clear();
Schema.Clear();
}
}
/// Get schema describing objects from reader.
/// Table from which extract column info.
/// Owner of table from which extract column info.
/// List of objects .
/// If your database doesn't get those values in upper case (like most of the databases) you should override this method.
protected virtual IList ReadSchema(string table, string owner)
{
using (var con = Open())
using (var cmd = con.CreateCommand()
.SetCommand(string.Format("SELECT * FROM {0}{1} WHERE 1 = 0",
!string.IsNullOrEmpty(owner) ? string.Format("{0}.", DecorateName(owner)) : string.Empty,
DecorateName(table))))
return ReadSchema(cmd)
.ToList();
}
/// Get schema describing objects from reader.
/// Command containing query to execute.
/// List of objects .
/// If your database doesn't get those values in upper case (like most of the databases) you should override this method.
protected virtual IEnumerable ReadSchema(IDbCommand cmd)
{
using (var rdr = cmd.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo))
using (var st = rdr.GetSchemaTable())
foreach (DataRow col in st.Rows)
{
var c = col.RowToDynamicUpper();
yield return new DynamicSchemaColumn
{
Name = c.COLUMNNAME,
Type = DynamicExtensions.TypeMap.TryGetNullable((Type)c.DATATYPE) ?? DbType.String,
IsKey = c.ISKEY ?? false,
IsUnique = c.ISUNIQUE ?? false,
Size = (int)(c.COLUMNSIZE ?? 0),
Precision = (byte)(c.NUMERICPRECISION ?? 0),
Scale = (byte)(c.NUMERICSCALE ?? 0)
};
}
}
private Dictionary BuildAndCacheSchema(string tableName, DynamicTypeMap mapper, string owner = null)
{
Dictionary schema = null;
if (mapper != null)
{
tableName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
mapper.Type.Name : mapper.Table.Name;
owner = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Owner) ?
null : mapper.Table.Owner;
}
bool databaseSchemaSupport = !string.IsNullOrEmpty(tableName) &&
(Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema;
bool mapperSchema = mapper != null && mapper.Table != null && (mapper.Table.Override || !databaseSchemaSupport);
#region Database schema
if (databaseSchemaSupport && !Schema.ContainsKey(tableName.ToLower()))
{
schema = ReadSchema(tableName, owner)
.Where(x => x.Name != null)
.DistinctBy(x => x.Name)
.ToDictionary(k => k.Name.ToLower(), k => k);
Schema[tableName.ToLower()] = schema;
}
#endregion Database schema
#region Type schema
if ((mapperSchema && !Schema.ContainsKey(mapper.Type.FullName)) ||
(schema == null && mapper != null && !mapper.Type.IsAnonymous()))
{
// TODO: Ged rid of this monster below...
if (databaseSchemaSupport)
{
#region Merge with db schema
schema = mapper.ColumnsMap.ToDictionary(k => k.Key, (v) =>
{
DynamicSchemaColumn? col = Schema[tableName.ToLower()].TryGetNullable(v.Key);
return new DynamicSchemaColumn
{
Name = DynamicExtensions.Coalesce(
v.Value.Column == null || string.IsNullOrEmpty(v.Value.Column.Name) ? null : v.Value.Column.Name,
col.HasValue && !string.IsNullOrEmpty(col.Value.Name) ? col.Value.Name : null,
v.Value.Name),
IsKey = DynamicExtensions.CoalesceNullable(
v.Value.Column != null ? v.Value.Column.IsKey : false,
col.HasValue ? col.Value.IsKey : false).Value,
Type = DynamicExtensions.CoalesceNullable(
v.Value.Column != null ? v.Value.Column.Type : null,
col.HasValue ? col.Value.Type : DynamicExtensions.TypeMap.TryGetNullable(v.Value.Type) ?? DbType.String).Value,
IsUnique = DynamicExtensions.CoalesceNullable(
v.Value.Column != null ? v.Value.Column.IsUnique : null,
col.HasValue ? col.Value.IsUnique : false).Value,
Size = DynamicExtensions.CoalesceNullable(
v.Value.Column != null ? v.Value.Column.Size : null,
col.HasValue ? col.Value.Size : 0).Value,
Precision = DynamicExtensions.CoalesceNullable(
v.Value.Column != null ? v.Value.Column.Precision : null,
col.HasValue ? col.Value.Precision : (byte)0).Value,
Scale = DynamicExtensions.CoalesceNullable(
v.Value.Column != null ? v.Value.Column.Scale : null,
col.HasValue ? col.Value.Scale : (byte)0).Value,
};
});
#endregion Merge with db schema
}
else
{
#region MapEnumerable based only on type
schema = mapper.ColumnsMap.ToDictionary(k => k.Key,
v => new DynamicSchemaColumn
{
Name = DynamicExtensions.Coalesce(v.Value.Column == null || string.IsNullOrEmpty(v.Value.Column.Name) ? null : v.Value.Column.Name, v.Value.Name),
IsKey = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.IsKey : false, false).Value,
Type = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Type : null, DynamicExtensions.TypeMap.TryGetNullable(v.Value.Type) ?? DbType.String).Value,
IsUnique = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.IsUnique : null, false).Value,
Size = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Size : null, 0).Value,
Precision = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Precision : null, 0).Value,
Scale = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.Scale : null, 0).Value,
});
#endregion MapEnumerable based only on type
}
}
if (mapper != null && schema != null)
Schema[mapper.Type.FullName] = schema;
#endregion Type schema
return schema;
}
#endregion Schema
#region Decorators
/// Gets or sets left side decorator for database objects.
public string LeftDecorator
{
get { return _leftDecorator; }
set
{
_leftDecorator = value;
_leftDecoratorIsInInvalidMembersChars =
_leftDecorator.Length == 1 && StringExtensions.InvalidMemberChars.Contains(_leftDecorator[0]);
}
}
/// Gets or sets right side decorator for database objects.
public string RightDecorator
{
get { return _rightDecorator; }
set
{
_rightDecorator = value;
_rightDecoratorIsInInvalidMembersChars =
_rightDecorator.Length == 1 && StringExtensions.InvalidMemberChars.Contains(_rightDecorator[0]);
}
}
/// Gets or sets parameter name format.
public string ParameterFormat { get { return _parameterFormat; } set { _parameterFormat = value; } }
/// Decorate string representing name of database object.
/// Name of database object.
/// Decorated name of database object.
public string DecorateName(string name)
{
return String.Concat(_leftDecorator, name, _rightDecorator);
}
/// Strip string representing name of database object from decorators.
/// Decorated name of database object.
/// Not decorated name of database object.
public string StripName(string name)
{
string res = name.Trim(StringExtensions.InvalidMemberChars);
if (!_leftDecoratorIsInInvalidMembersChars && res.StartsWith(_leftDecorator))
res = res.Substring(_leftDecorator.Length);
if (!_rightDecoratorIsInInvalidMembersChars && res.EndsWith(_rightDecorator))
res = res.Substring(0, res.Length - _rightDecorator.Length);
return res;
}
/// Decorate string representing name of database object.
/// String builder to which add decorated name.
/// Name of database object.
public void DecorateName(StringBuilder sb, string name)
{
sb.Append(_leftDecorator);
sb.Append(name);
sb.Append(_rightDecorator);
}
/// Get database parameter name.
/// Friendly parameter name or number.
/// Formatted parameter name.
public string GetParameterName(object parameter)
{
return String.Format(_parameterFormat, parameter).Replace(" ", "_");
}
/// Get database parameter name.
/// String builder to which add parameter name.
/// Friendly parameter name or number.
public void GetParameterName(StringBuilder sb, object parameter)
{
sb.AppendFormat(_parameterFormat, parameter.ToString().Replace(" ", "_"));
}
/// Dumps the command into console output.
/// The command to dump.
public virtual void DumpCommand(IDbCommand cmd)
{
cmd.Dump(Console.Out);
}
#endregion Decorators
#region Connection
/// Open managed connection.
/// Opened connection.
public IDbConnection Open()
{
IDbConnection conn = null;
DynamicConnection ret = null;
bool opened = false;
lock (SyncLock)
{
if (_tempConn == null)
{
if (TransactionPool.Count == 0 || !_singleConnection)
{
conn = _provider.CreateConnection();
conn.ConnectionString = _connectionString;
conn.Open();
opened = true;
TransactionPool.Add(conn, new Stack());
CommandsPool.Add(conn, new List());
}
else
{
conn = TransactionPool.Keys.First();
if (conn.State != ConnectionState.Open)
{
conn.Open();
opened = true;
}
}
ret = new DynamicConnection(this, conn, _singleTransaction);
}
else
ret = _tempConn;
}
if (opened)
ExecuteInitCommands(ret);
return ret;
}
/// Close connection if we are allowed to.
/// Connection to manage.
internal void Close(IDbConnection connection)
{
if (connection == null)
return;
if (!_singleConnection && connection != null && TransactionPool.ContainsKey(connection))
{
// Close all commands
if (CommandsPool.ContainsKey(connection))
{
var tmp = CommandsPool[connection].ToList();
tmp.ForEach(cmd => cmd.Dispose());
CommandsPool[connection].Clear();
}
// Rollback remaining transactions
while (TransactionPool[connection].Count > 0)
{
IDbTransaction trans = TransactionPool[connection].Pop();
trans.Rollback();
trans.Dispose();
}
// Close connection
if (connection.State == ConnectionState.Open)
connection.Close();
// remove from pools
lock (SyncLock)
{
TransactionPool.Remove(connection);
CommandsPool.Remove(connection);
}
// Set stamp
_poolStamp = DateTime.Now.Ticks;
// Dispose the corpse
connection.Dispose();
connection = null;
}
}
/// Gets or sets contains commands executed when connection is opened.
public List InitCommands { get; set; }
private void ExecuteInitCommands(IDbConnection conn)
{
if (InitCommands != null)
using (IDbCommand command = conn.CreateCommand())
foreach (string commandText in InitCommands)
command
.SetCommand(commandText)
.ExecuteNonQuery();
}
#endregion Connection
#region Transaction
/// Begins a global database transaction.
/// Using this method connection is set to single open
/// connection until all transactions are finished.
/// Returns representation.
public IDbTransaction BeginTransaction()
{
_tempConn = Open() as DynamicConnection;
return _tempConn.BeginTransaction(null, null, () =>
{
var t = TransactionPool.TryGetValue(_tempConn.Connection);
if (t == null | t.Count == 0)
{
_tempConn.Dispose();
_tempConn = null;
}
});
}
/// Begins a database transaction with the specified
/// value.
/// One of the values.
/// Returns representation.
public IDbTransaction BeginTransaction(IsolationLevel il)
{
_tempConn = Open() as DynamicConnection;
return _tempConn.BeginTransaction(il, null, () =>
{
var t = TransactionPool.TryGetValue(_tempConn.Connection);
if (t == null | t.Count == 0)
{
_tempConn.Dispose();
_tempConn = null;
}
});
}
/// Begins a database transaction with the specified
/// value.
/// Custom parameter describing transaction options.
/// Returns representation.
public IDbTransaction BeginTransaction(object custom)
{
_tempConn = Open() as DynamicConnection;
return _tempConn.BeginTransaction(null, custom, () =>
{
var t = TransactionPool.TryGetValue(_tempConn.Connection);
if (t == null | t.Count == 0)
{
_tempConn.Dispose();
_tempConn = null;
}
});
}
#endregion Transaction
#region IExtendedDisposable Members
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
public void Dispose()
{
#if !DYNAMORM_OMMIT_OLDSYNTAX
var tables = TablesCache.Values.ToList();
TablesCache.Clear();
tables.ForEach(t => t.Dispose());
tables.Clear();
tables = null;
#endif
foreach (var con in TransactionPool)
{
// Close all commands
if (CommandsPool.ContainsKey(con.Key))
{
var tmp = CommandsPool[con.Key].ToList();
tmp.ForEach(cmd => cmd.Dispose());
CommandsPool[con.Key].Clear();
tmp.Clear();
CommandsPool[con.Key] = tmp = null;
}
// Rollback remaining transactions
while (con.Value.Count > 0)
{
IDbTransaction trans = con.Value.Pop();
trans.Rollback();
trans.Dispose();
}
// Close connection
if (con.Key.State == ConnectionState.Open)
con.Key.Close();
// Dispose it
con.Key.Dispose();
}
while (RemainingBuilders.Count > 0)
RemainingBuilders.First().Dispose();
// Clear pools
lock (SyncLock)
{
TransactionPool.Clear();
CommandsPool.Clear();
RemainingBuilders.Clear();
TransactionPool = null;
CommandsPool = null;
RemainingBuilders = null;
}
ClearSchema();
IsDisposed = true;
}
/// Gets a value indicating whether this instance is disposed.
public bool IsDisposed { get; private set; }
#endregion IExtendedDisposable Members
}
/// Represents database connection options.
[Flags]
[System.Reflection.ObfuscationAttribute(Feature = "renaming", ApplyToMembers = true)]
public enum DynamicDatabaseOptions
{
/// No specific options.
None = 0x00000000,
/// Only single persistent database connection.
SingleConnection = 0x00000001,
/// Only one transaction.
SingleTransaction = 0x00000002,
/// Database supports top syntax (SELECT TOP x ... FROM ...).
SupportTop = 0x00000080,
/// Database supports limit offset syntax (SELECT ... FROM ... LIMIT x OFFSET y).
SupportLimitOffset = 0x00000040,
/// Database support standard schema.
SupportSchema = 0x00000010,
/// Database support stored procedures (EXEC procedure ...).
SupportStoredProcedures = 0x00000020,
/// Debug option allowing to enable command dumps by default.
DumpCommands = 0x01000000,
}
/// Dynamic expando is a simple and temporary class to resolve memory leaks inside ExpandoObject.
public class DynamicExpando : DynamicObject, IDictionary, ICollection>, IEnumerable>, IEnumerable
{
/// Class containing information about last accessed property of dynamic object.
public class PropertyAccess
{
/// Enum describing type of access to object.
public enum TypeOfAccess
{
/// Get member.
Get,
/// Set member.
Set,
}
/// Gets the type of operation.
public TypeOfAccess Operation { get; internal set; }
/// Gets the name of property.
public string Name { get; internal set; }
/// Gets the type from binder.
public Type RequestedType { get; internal set; }
/// Gets the type of value stored in object.
public Type Type { get; internal set; }
/// Gets the value stored in object.
public object Value { get; internal set; }
}
private Dictionary _data = new Dictionary();
private PropertyAccess _lastProp = new PropertyAccess();
/// Initializes a new instance of the class.
public DynamicExpando()
{
}
/// Gets the last accesses property.
/// Description of last accessed property.
public PropertyAccess GetLastAccessesProperty()
{
return _lastProp;
}
/// Tries to get member value.
/// Returns true , if get member was tried, false otherwise.
/// The context binder.
/// The invocation result.
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _data.TryGetValue(binder.Name);
_lastProp.Operation = PropertyAccess.TypeOfAccess.Get;
_lastProp.RequestedType = binder.ReturnType;
_lastProp.Name = binder.Name;
_lastProp.Value = result;
_lastProp.Type = result == null ? typeof(void) : result.GetType();
return true;
}
/// Tries to set member.
/// Returns true , if set member was tried, false otherwise.
/// The context binder.
/// Value which will be set.
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_data[binder.Name] = value;
_lastProp.Operation = PropertyAccess.TypeOfAccess.Set;
_lastProp.RequestedType = binder.ReturnType;
_lastProp.Name = binder.Name;
_lastProp.Value = value;
_lastProp.Type = value == null ? typeof(void) : value.GetType();
return true;
}
#region IDictionary implementation
bool IDictionary.ContainsKey(string key)
{
return _data.ContainsKey(key);
}
void IDictionary.Add(string key, object value)
{
_data.Add(key, value);
}
bool IDictionary.Remove(string key)
{
return _data.Remove(key);
}
bool IDictionary.TryGetValue(string key, out object value)
{
return _data.TryGetValue(key, out value);
}
object IDictionary.this[string index] { get { return _data[index]; } set { _data[index] = value; } }
ICollection IDictionary.Keys { get { return _data.Keys; } }
ICollection IDictionary.Values { get { return _data.Values; } }
#endregion IDictionary implementation
#region ICollection implementation
void ICollection>.Add(KeyValuePair item)
{
((ICollection>)_data).Add(item);
}
void ICollection>.Clear()
{
_data.Clear();
}
bool ICollection>.Contains(KeyValuePair item)
{
return ((ICollection>)_data).Contains(item);
}
void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
{
((ICollection>)_data).CopyTo(array, arrayIndex);
}
bool ICollection>.Remove(KeyValuePair item)
{
return ((ICollection>)_data).Remove(item);
}
int ICollection>.Count { get { return _data.Count; } }
bool ICollection>.IsReadOnly { get { return ((ICollection>)_data).IsReadOnly; } }
#endregion ICollection implementation
#region IEnumerable implementation
IEnumerator> IEnumerable>.GetEnumerator()
{
return _data.GetEnumerator();
}
#endregion IEnumerable implementation
#region IEnumerable implementation
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_data).GetEnumerator();
}
#endregion IEnumerable implementation
}
/// Extension to ORM objects.
public static class DynamicExtensions
{
#region Type column map
/// MapEnumerable of .NET types to .
public static readonly Dictionary TypeMap = new Dictionary()
{
{ typeof(byte), DbType.Byte },
{ typeof(sbyte), DbType.SByte },
{ typeof(short), DbType.Int16 },
{ typeof(ushort), DbType.UInt16 },
{ typeof(int), DbType.Int32 },
{ typeof(uint), DbType.UInt32 },
{ typeof(long), DbType.Int64 },
{ typeof(ulong), DbType.UInt64 },
{ typeof(float), DbType.Single },
{ typeof(double), DbType.Double },
{ typeof(decimal), DbType.Decimal },
{ typeof(bool), DbType.Boolean },
{ typeof(string), DbType.String },
{ typeof(char), DbType.StringFixedLength },
{ typeof(Guid), DbType.Guid },
{ typeof(DateTime), DbType.DateTime },
{ typeof(DateTimeOffset), DbType.DateTimeOffset },
{ typeof(byte[]), DbType.Binary },
{ typeof(byte?), DbType.Byte },
{ typeof(sbyte?), DbType.SByte },
{ typeof(short?), DbType.Int16 },
{ typeof(ushort?), DbType.UInt16 },
{ typeof(int?), DbType.Int32 },
{ typeof(uint?), DbType.UInt32 },
{ typeof(long?), DbType.Int64 },
{ typeof(ulong?), DbType.UInt64 },
{ typeof(float?), DbType.Single },
{ typeof(double?), DbType.Double },
{ typeof(decimal?), DbType.Decimal },
{ typeof(bool?), DbType.Boolean },
{ typeof(char?), DbType.StringFixedLength },
{ typeof(Guid?), DbType.Guid },
{ typeof(DateTime?), DbType.DateTime },
{ typeof(DateTimeOffset?), DbType.DateTimeOffset }
};
#endregion Type column map
#region Command extensions
/// Set connection on the fly.
/// in which changes will be made.
/// which will be set to instance.
/// Returns edited instance.
public static IDbCommand SetConnection(this IDbCommand command, IDbConnection connection)
{
command.Connection = connection;
return command;
}
/// Set connection on the fly.
/// in which changes will be made.
/// which will be set to instance.
/// Returns edited instance.
public static IDbCommand SetTransaction(this IDbCommand command, IDbTransaction transaction)
{
command.Transaction = transaction;
return command;
}
#region SetCommand
/// Set properties on the fly.
/// in which changes will be made.
/// Indicates or specifies how the property is interpreted.
/// The wait time before terminating the attempt to execute a command and generating an error.
/// The text command to run against the data source.
/// Arguments used to format command.
/// Returns edited instance.
public static IDbCommand SetCommand(this IDbCommand command, CommandType commandType, int commandTimeout, string commandText, params object[] args)
{
command.CommandType = commandType;
command.CommandTimeout = commandTimeout;
if (args != null && args.Length > 0)
command.CommandText = string.Format(commandText, args);
else
command.CommandText = commandText;
return command;
}
/// Set properties on the fly.
/// in which changes will be made.
/// The wait time before terminating the attempt to execute a command and generating an error.
/// The text command to run against the data source.
/// Arguments used to format command.
/// Returns edited instance.
public static IDbCommand SetCommand(this IDbCommand command, int commandTimeout, string commandText, params object[] args)
{
command.CommandTimeout = commandTimeout;
if (args != null && args.Length > 0)
command.CommandText = string.Format(commandText, args);
else
command.CommandText = commandText;
return command;
}
/// Set properties on the fly.
/// in which changes will be made.
/// Indicates or specifies how the property is interpreted.
/// The text command to run against the data source.
/// Arguments used to format command.
/// Returns edited instance.
public static IDbCommand SetCommand(this IDbCommand command, CommandType commandType, string commandText, params object[] args)
{
command.CommandType = commandType;
if (args != null && args.Length > 0)
command.CommandText = string.Format(commandText, args);
else
command.CommandText = commandText;
return command;
}
/// Set properties on the fly.
/// in which changes will be made.
/// The text command to run against the data source.
/// Arguments used to format command.
/// Returns edited instance.
public static IDbCommand SetCommand(this IDbCommand command, string commandText, params object[] args)
{
if (args != null && args.Length > 0)
command.CommandText = string.Format(commandText, args);
else
command.CommandText = commandText;
return command;
}
/// Set properties on the fly.
/// in which changes will be made.
/// Command builder.
/// Returns edited instance.
public static IDbCommand SetCommand(this IDbCommand command, IDynamicQueryBuilder builder)
{
builder.FillCommand(command);
return command;
}
#endregion SetCommand
#region AddParameter
/// Extension method for adding in a bunch of parameters.
/// Command to handle.
/// Database object required to get proper formatting.
/// Items to add.
/// Returns edited instance.
public static IDbCommand AddParameters(this IDbCommand cmd, DynamicDatabase database, params object[] args)
{
if (args != null && args.Count() > 0)
foreach (var item in args)
cmd.AddParameter(database, item);
return cmd;
}
/// Extension method for adding in a bunch of parameters.
/// Command to handle.
/// Database object required to get proper formatting.
/// Items to add in an expando object.
/// Returns edited instance.
public static IDbCommand AddParameters(this IDbCommand cmd, DynamicDatabase database, ExpandoObject args)
{
if (args != null && args.Count() > 0)
foreach (var item in args.ToDictionary())
cmd.AddParameter(database, item.Key, item.Value);
return cmd;
}
/// Extension method for adding in a bunch of parameters.
/// Command to handle.
/// Database object required to get proper formatting.
/// Items to add in an expando object.
/// Returns edited instance.
public static IDbCommand AddParameters(this IDbCommand cmd, DynamicDatabase database, DynamicExpando args)
{
if (args != null && args.Count() > 0)
foreach (var item in args.ToDictionary())
cmd.AddParameter(database, item.Key, item.Value);
return cmd;
}
/// Extension for adding single parameter determining only type of object.
/// Command to handle.
/// Database object required to get proper formatting.
/// Items to add.
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand cmd, DynamicDatabase database, object item)
{
return cmd.AddParameter(database, database.GetParameterName(cmd.Parameters.Count), item);
}
/// Extension for adding single parameter determining only type of object.
/// Command to handle.
/// Database object required to get proper formatting.
/// Name of parameter.
/// Items to add.
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand cmd, DynamicDatabase database, string name, object item)
{
var p = cmd.CreateParameter();
p.ParameterName = name;
if (item == null || item == DBNull.Value)
p.Value = DBNull.Value;
else
{
Type type = item.GetType();
p.DbType = TypeMap.TryGetNullable(type) ?? DbType.String;
if (type == typeof(DynamicExpando) || type == typeof(ExpandoObject))
p.Value = ((IDictionary)item).Values.FirstOrDefault();
else
p.Value = item;
if (p.DbType == DbType.String)
p.Size = item.ToString().Length > 4000 ? -1 : 4000;
}
cmd.Parameters.Add(p);
return cmd;
}
/// Extension for adding single parameter determining only type of object.
/// Command to handle.
/// Query builder containing schema.
/// Column schema to use.
/// Parameter value.
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand cmd, IDynamicQueryBuilder builder, DynamicSchemaColumn? col, object value)
{
var p = cmd.CreateParameter();
p.ParameterName = builder.Database.GetParameterName(cmd.Parameters.Count);
if (col.HasValue)
{
p.DbType = col.Value.Type;
if ((builder.Database.Options & DynamicDatabaseOptions.SupportSchema) == DynamicDatabaseOptions.SupportSchema)
{
p.Size = col.Value.Size;
p.Precision = col.Value.Precision;
p.Scale = col.Value.Scale;
// Quick fix - review that
// Quick fix 2 - use item.Schema in that case
if (p.Scale > p.Precision)
p.Scale = 4;
}
p.Value = value == null ? DBNull.Value : value;
}
else if (value == null || value == DBNull.Value)
p.Value = DBNull.Value;
else
{
p.DbType = TypeMap.TryGetNullable(value.GetType()) ?? DbType.String;
if (p.DbType == DbType.String)
p.Size = value.ToString().Length > 4000 ? -1 : 4000;
p.Value = value;
}
cmd.Parameters.Add(p);
return cmd;
}
/// Extension for adding single parameter determining only type of object.
/// Command to handle.
/// Query builder containing schema.
/// Column item to add.
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand cmd, IDynamicQueryBuilder builder, DynamicColumn item)
{
var p = cmd.CreateParameter();
p.ParameterName = builder.Database.GetParameterName(cmd.Parameters.Count);
var col = item.Schema ?? (builder as DynamicQueryBuilder)
.NullOr(b => b.GetColumnFromSchema(item.ColumnName),
builder.Tables.FirstOrDefault()
.NullOr(t => t.Schema
.NullOr(s => s.TryGetNullable(item.ColumnName.ToLower()), null), null));
if (col.HasValue)
{
p.DbType = col.Value.Type;
if (builder.SupportSchema)
{
p.Size = col.Value.Size;
p.Precision = col.Value.Precision;
p.Scale = col.Value.Scale;
// Quick fix - review that
// Quick fix 2 - use item.Schema in that case
if (p.Scale > p.Precision)
p.Scale = 4;
}
p.Value = item.Value == null ? DBNull.Value : item.Value;
}
else if (item.Value == null || item.Value == DBNull.Value)
p.Value = DBNull.Value;
else
{
p.DbType = TypeMap.TryGetNullable(item.Value.GetType()) ?? DbType.String;
if (p.DbType == DbType.String)
p.Size = item.Value.ToString().Length > 4000 ? -1 : 4000;
p.Value = item.Value;
}
cmd.Parameters.Add(p);
return cmd;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// Value indicating whether the parameter is input-only, output-only, bidirectional, or a stored procedure return value .
/// The of the .
/// The size of the parameter.
/// Indicates the precision of numeric parameters.
/// Indicates the scale of numeric parameters.
/// The value of the .
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, ParameterDirection parameterDirection, DbType databaseType, int size, byte precision, byte scale, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.Direction = parameterDirection;
param.DbType = databaseType;
param.Size = size;
param.Precision = precision;
param.Scale = scale;
param.Value = value;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// Value indicating whether the parameter is input-only, output-only, bidirectional, or a stored procedure return value .
/// The of the .
/// The size of the parameter.
/// Indicates the precision of numeric parameters.
/// Indicates the scale of numeric parameters.
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, ParameterDirection parameterDirection, DbType databaseType, int size, byte precision, byte scale)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.Direction = parameterDirection;
param.DbType = databaseType;
param.Size = size;
param.Precision = precision;
param.Scale = scale;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// Value indicating whether the parameter is input-only, output-only, bidirectional, or a stored procedure return value .
/// The of the .
/// Indicates the precision of numeric parameters.
/// Indicates the scale of numeric parameters.
/// The value of the .
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, ParameterDirection parameterDirection, DbType databaseType, byte precision, byte scale, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.Direction = parameterDirection;
param.DbType = databaseType;
param.Precision = precision;
param.Scale = scale;
param.Value = value;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// The of the .
/// Indicates the precision of numeric parameters.
/// Indicates the scale of numeric parameters.
/// The value of the .
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, DbType databaseType, byte precision, byte scale, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.DbType = databaseType;
param.Precision = precision;
param.Scale = scale;
param.Value = value;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// The of the .
/// Indicates the precision of numeric parameters.
/// Indicates the scale of numeric parameters.
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, DbType databaseType, byte precision, byte scale)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.DbType = databaseType;
param.Precision = precision;
param.Scale = scale;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// Value indicating whether the parameter is input-only, output-only, bidirectional, or a stored procedure return value .
/// The of the .
/// The size of the parameter.
/// The value of the .
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, ParameterDirection parameterDirection, DbType databaseType, int size, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.Direction = parameterDirection;
param.DbType = databaseType;
param.Size = size;
param.Value = value;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// The of the .
/// The size of the parameter.
/// The value of the .
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, DbType databaseType, int size, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.DbType = databaseType;
param.Size = size;
param.Value = value;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// The of the .
/// The value of the .
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, DbType databaseType, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.DbType = databaseType;
param.Value = value;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// The of the .
/// The size of the parameter.
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, DbType databaseType, int size)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.DbType = databaseType;
param.Size = size;
command.Parameters.Add(param);
return command;
}
/// Add to on the fly.
/// to which parameter will be added.
/// The name of the .
/// The of the .
/// Returns edited instance.
public static IDbCommand AddParameter(this IDbCommand command, string parameterName, DbType databaseType)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameterName;
param.DbType = databaseType;
command.Parameters.Add(param);
return command;
}
#endregion AddParameter
#region SetParameter
/// Set value for on the fly.
/// to which parameter will be added.
/// The name of the .
/// Value to set on this parameter.
/// Returns edited instance.
public static IDbCommand SetParameter(this IDbCommand command, string parameterName, object value)
{
((IDbDataParameter)command.Parameters[parameterName]).Value = value;
return command;
}
/// Set value for on the fly.
/// to which parameter will be added.
/// Index of the .
/// Value to set on this parameter.
/// Returns edited instance.
public static IDbCommand SetParameter(this IDbCommand command, int index, object value)
{
((IDbDataParameter)command.Parameters[index]).Value = value;
return command;
}
#endregion SetParameter
#region Generic Execution
#if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE
/// Execute scalar and return string if possible.
/// Type to parse to.
/// which will be executed.
/// Returns resulting instance of T from query.
public static T ExecuteScalarAs(this IDbCommand command)
{
return ExecuteScalarAs(command, default(T), null);
}
/// Execute scalar and return string if possible.
/// Type to parse to.
/// which will be executed.
/// Handler of a try parse method.
/// Returns resulting instance of T from query.
public static T ExecuteScalarAs(this IDbCommand command, DynamicExtensions.TryParseHandler handler)
{
return ExecuteScalarAs(command, default(T), handler);
}
/// Execute scalar and return string if possible.
/// Type to parse to.
/// which will be executed.
/// Default result value.
/// Returns resulting instance of T from query.
public static T ExecuteScalarAs(this IDbCommand command, T defaultValue)
{
return ExecuteScalarAs(command, defaultValue, null);
}
/// Execute scalar and return string if possible.
/// Type to parse to.
/// which will be executed.
/// Default result value.
/// Handler of a try parse method.
/// Returns resulting instance of T from query.
public static T ExecuteScalarAs(this IDbCommand command, T defaultValue, DynamicExtensions.TryParseHandler handler)
{
T ret = defaultValue;
object o = command.ExecuteScalar();
if (o is T)
return (T)o;
else if (o != DBNull.Value && o != null)
{
if (handler != null)
ret = o.ToString().TryParseDefault(defaultValue, handler);
else if (o is IConvertible && typeof(T).GetInterfaces().Any(i => i == typeof(IConvertible)))
ret = (T)Convert.ChangeType(o, typeof(T));
else if (typeof(T) == typeof(Guid))
{
if (o.GetType() == typeof(byte[]))
ret = (T)(object)new Guid((byte[])o);
else
ret = (T)(object)Guid.Parse(o.ToString());
}
else if (typeof(T) == typeof(string))
ret = (T)(o.ToString() as object);
else if (typeof(T) == typeof(object))
ret = (T)o;
else
{
var method = typeof(T).GetMethod(
"TryParse",
new Type[]
{
typeof(string),
Type.GetType(string.Format("{0}&", typeof(T).FullName))
});
if (method != null)
ret = o.ToString().TryParseDefault(defaultValue, delegate(string v, out T r)
{
r = defaultValue;
return (bool)method.Invoke(null, new object[] { v, r });
});
else
throw new InvalidOperationException("Provided type can't be parsed using generic approach.");
}
}
return ret;
}
/// Execute enumerator of specified type.
/// Type to parse to.
/// which will be executed.
/// Default result value.
/// Handler of a try parse method.
/// Returns enumerator of specified type from query.
public static IEnumerable ExecuteEnumeratorOf(this IDbCommand command, T defaultValue, DynamicExtensions.TryParseHandler handler) where T : struct
{
using (IDataReader reader = command.ExecuteReader())
{
var method = typeof(T).GetMethod(
"TryParse",
new[]
{
typeof(string),
Type.GetType(string.Format("{0}&", typeof(T).FullName))
});
while (reader.Read())
{
T ret = defaultValue;
if (!reader.IsDBNull(0))
{
object o = reader.GetValue(0);
if (o is T)
ret = (T)o;
else if (o != DBNull.Value)
{
if (handler != null)
ret = o.ToString().TryParseDefault(defaultValue, handler);
else if (method != null)
ret = o.ToString().TryParseDefault(defaultValue, delegate(string v, out T r)
{
r = defaultValue;
return (bool)method.Invoke(null, new object[] { v, r });
});
else if (typeof(T) == typeof(string))
ret = (T)(o.ToString() as object);
else if (typeof(T) == typeof(object))
ret = (T)o;
else
throw new InvalidOperationException("Provided type can't be parsed using generic approach.");
}
}
yield return ret;
}
}
}
#endif
#endregion Generic Execution
/// Dump command into string.
/// Command to dump.
/// Returns dumped instance in string form.
public static string DumpToString(this IDbCommand command)
{
var sb = new StringBuilder();
command.Dump(sb);
return sb.ToString();
}
/// Dump command into text writer.
/// Command to dump.
/// Builder to which write output.
/// Returns dumped instance.
public static IDbCommand Dump(this IDbCommand command, StringBuilder buider)
{
using (StringWriter sw = new StringWriter(buider))
return command.Dump(sw);
}
/// Dump command into text writer.
/// Command to dump.
/// Writer to which write output.
/// Returns dumped instance.
public static IDbCommand Dump(this IDbCommand command, TextWriter writer)
{
writer.WriteLine("Type: {0}; Timeout: {1}; Query: {2}", command.CommandType, command.CommandTimeout, command.CommandText);
if (command.Parameters.Count > 0)
{
writer.WriteLine("Parameters:");
foreach (IDbDataParameter param in command.Parameters)
{
writer.WriteLine(" '{0}' ({1} (s:{2} p:{3} s:{4})) = '{5}' ({6});",
param.ParameterName,
param.DbType,
param.Scale,
param.Precision,
param.Scale,
param.Value is byte[] ? ConvertByteArrayToHexString((byte[])param.Value) : param.Value ?? "NULL",
param.Value != null ? param.Value.GetType().Name : "DBNull");
}
writer.WriteLine();
}
return command;
}
/// Convert byte array to hex formatted string without separators.
/// Byte Array Data.
/// Hex string representation of byte array.
private static string ConvertByteArrayToHexString(byte[] data)
{
return ConvertByteArrayToHexString(data, 0);
}
/// Convert byte array to hex formatted string.
/// Byte Array Data.
/// Put '-' each separatorEach characters.
/// Hex string representation of byte array.
private static string ConvertByteArrayToHexString(byte[] data, int separatorEach)
{
int len = data.Length * 2;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < len; i++)
{
sb.AppendFormat("{0:X}", data[(i / 2)] >> (((i % 2) == 0) ? 4 : 0) & 0x0F);
if ((separatorEach > 0) && ((i + 1) % separatorEach == 0))
sb.AppendFormat("-");
}
return sb.ToString();
}
#endregion Command extensions
#region Dynamic builders extensions
/// Turns an to a Dynamic list of things.
/// Ready to execute builder.
/// List of things.
public static List ToList(this IDynamicSelectQueryBuilder b)
{
return b.Execute().ToList();
}
/// Turns an to a Dynamic list of things with specified type.
/// Type of object to map on.
/// Ready to execute builder.
/// List of things.
public static List ToList(this IDynamicSelectQueryBuilder b) where T : class
{
return b.Execute().ToList();
}
/// Sets the on create temporary parameter action.
/// Class implementing interface.
/// The builder on which set delegate.
/// Action to invoke.
/// Returns instance of builder on which action is set.
public static T CreateTemporaryParameterAction(this T b, Action a) where T : IDynamicQueryBuilder
{
if (a == null)
b.OnCreateTemporaryParameter = null;
else
{
if (b.OnCreateTemporaryParameter == null)
b.OnCreateTemporaryParameter = new List>();
b.OnCreateTemporaryParameter.Add(a);
}
return b;
}
/// Sets the on create real parameter action.
/// Class implementing interface.
/// The builder on which set delegate.
/// Action to invoke.
/// Returns instance of builder on which action is set.
public static T CreateParameterAction(this T b, Action a) where T : IDynamicQueryBuilder
{
if (a == null)
b.OnCreateParameter = null;
else
{
if (b.OnCreateParameter == null)
b.OnCreateParameter = new List>();
b.OnCreateParameter.Add(a);
}
return b;
}
/// Sets the virtual mode on builder.
/// Class implementing interface.
/// The builder on which set virtual mode.
/// Virtual mode.
/// Returns instance of builder on which virtual mode is set.
public static T SetVirtualMode(this T b, bool virtualMode) where T : IDynamicQueryBuilder
{
b.VirtualMode = virtualMode;
return b;
}
/// Creates sub query that can be used inside of from/join/expressions.
/// Class implementing interface.
/// The builder that will be parent of new sub query.
/// Instance of sub query.
public static IDynamicSelectQueryBuilder SubQuery(this T b) where T : IDynamicQueryBuilder
{
return new DynamicSelectQueryBuilder(b.Database, b as DynamicQueryBuilder);
}
/// Creates sub query that can be used inside of from/join/expressions.
/// Class implementing interface.
/// The builder that will be parent of new sub query.
/// The specification for sub query.
/// The specification for sub query.
/// Instance of sub query.
public static IDynamicSelectQueryBuilder SubQuery(this T b, Func fn, params Func[] func) where T : IDynamicQueryBuilder
{
return new DynamicSelectQueryBuilder(b.Database, b as DynamicQueryBuilder).From(fn, func);
}
/// Creates sub query that can be used inside of from/join/expressions.
/// Class implementing interface.
/// The builder that will be parent of new sub query.
/// First argument is parent query, second one is a sub query.
/// This instance to permit chaining.
public static T SubQuery(this T b, Action subquery) where T : IDynamicQueryBuilder
{
var sub = b.SubQuery();
subquery(b, sub);
(b as DynamicQueryBuilder).ParseCommand(sub as DynamicQueryBuilder, b.Parameters);
return b;
}
/// Creates sub query that can be used inside of from/join/expressions.
/// Class implementing interface.
/// The builder that will be parent of new sub query.
/// First argument is parent query, second one is a sub query.
/// The specification for sub query.
/// The specification for sub query.
/// This instance to permit chaining.
public static T SubQuery(this T b, Action subquery, Func fn, params Func[] func) where T : IDynamicQueryBuilder
{
var sub = b.SubQuery(fn, func);
subquery(b, sub);
(b as DynamicQueryBuilder).ParseCommand(sub as DynamicQueryBuilder, b.Parameters);
return b;
}
#endregion Dynamic builders extensions
#region Dynamic extensions
/// Turns an to a Dynamic list of things.
/// Reader from which read data.
/// List of things.
public static List ToList(this IDataReader r)
{
var result = new List();
while (r.Read())
result.Add(r.RowToDynamic());
return result;
}
/// Turns the dictionary into an ExpandoObject.
/// Dictionary to convert.
/// Converted dictionary.
public static dynamic ToDynamic(this IDictionary d)
{
var result = new DynamicExpando();
var dict = (IDictionary)result;
foreach (var prop in d)
dict.Add(prop.Key, prop.Value);
return result;
}
/// Turns the dictionary into an ExpandoObject.
/// Dictionary to convert.
/// Converted dictionary.
public static dynamic ToExpando(this IDictionary d)
{
var result = new ExpandoObject();
var dict = (IDictionary)result;
foreach (var prop in d)
dict.Add(prop.Key, prop.Value);
return result;
}
/// Turns the object into an ExpandoObject.
/// Object to convert.
/// Converted object.
public static dynamic ToDynamic(this object o)
{
var ot = o.GetType();
if (ot == typeof(DynamicExpando) || ot == typeof(ExpandoObject))
return o;
var result = new DynamicExpando();
var dict = (IDictionary)result;
if (o is IDictionary)
((IDictionary)o)
.ToList()
.ForEach(kvp => dict.Add(kvp.Key, kvp.Value));
else if (ot == typeof(NameValueCollection) || ot.IsSubclassOf(typeof(NameValueCollection)))
{
var nameValue = (NameValueCollection)o;
nameValue.Cast()
.Select(key => new KeyValuePair(key, nameValue[key]))
.ToList()
.ForEach(i => dict.Add(i));
}
else
{
var mapper = DynamicMapperCache.GetMapper(ot);
if (mapper != null)
{
foreach (var item in mapper.ColumnsMap.Values)
if (item.Get != null)
dict.Add(item.Name, item.Get(o));
}
else
{
var props = ot.GetProperties();
foreach (var item in props)
if (item.CanRead)
dict.Add(item.Name, item.GetValue(o, null));
}
}
return result;
}
/// Turns the object into an ExpandoObject.
/// Object to convert.
/// Converted object.
public static dynamic ToExpando(this object o)
{
var ot = o.GetType();
if (ot == typeof(ExpandoObject) || ot == typeof(DynamicExpando))
return o;
var result = new ExpandoObject();
var dict = (IDictionary)result;
if (o is IDictionary)
((IDictionary)o)
.ToList()
.ForEach(kvp => dict.Add(kvp.Key, kvp.Value));
else if (ot == typeof(NameValueCollection) || ot.IsSubclassOf(typeof(NameValueCollection)))
{
var nameValue = (NameValueCollection)o;
nameValue.Cast()
.Select(key => new KeyValuePair(key, nameValue[key]))
.ToList()
.ForEach(i => dict.Add(i));
}
else
{
var mapper = DynamicMapperCache.GetMapper(ot);
if (mapper != null)
{
foreach (var item in mapper.ColumnsMap.Values)
if (item.Get != null)
dict.Add(item.Name, item.Get(o));
}
else
{
var props = ot.GetProperties();
foreach (var item in props)
if (item.CanRead)
dict.Add(item.Name, item.GetValue(o, null));
}
}
return result;
}
/// Convert data row row into dynamic object.
/// DataRow from which read.
/// Generated dynamic object.
public static dynamic RowToDynamic(this DataRow r)
{
dynamic e = new DynamicExpando();
int len = r.Table.Columns.Count;
for (int i = 0; i < len; i++)
((IDictionary)e).Add(r.Table.Columns[i].ColumnName, r.IsNull(i) ? null : r[i]);
return e;
}
/// Convert data row row into dynamic object.
/// DataRow from which read.
/// Generated dynamic object.
public static dynamic RowToExpando(this DataRow r)
{
dynamic e = new ExpandoObject();
int len = r.Table.Columns.Count;
for (int i = 0; i < len; i++)
((IDictionary)e).Add(r.Table.Columns[i].ColumnName, r.IsNull(i) ? null : r[i]);
return e;
}
/// Convert data row row into dynamic object (upper case key).
/// DataRow from which read.
/// Generated dynamic object.
public static dynamic RowToDynamicUpper(this DataRow r)
{
dynamic e = new DynamicExpando();
int len = r.Table.Columns.Count;
for (int i = 0; i < len; i++)
((IDictionary)e).Add(r.Table.Columns[i].ColumnName.ToUpper(), r.IsNull(i) ? null : r[i]);
return e;
}
/// Convert data row row into dynamic object (upper case key).
/// DataRow from which read.
/// Generated dynamic object.
public static dynamic RowToExpandoUpper(this DataRow r)
{
// ERROR: Memory leak
dynamic e = new ExpandoObject();
int len = r.Table.Columns.Count;
for (int i = 0; i < len; i++)
((IDictionary)e).Add(r.Table.Columns[i].ColumnName.ToUpper(), r.IsNull(i) ? null : r[i]);
return e;
}
internal static Dictionary RowToDynamicUpperDict(this DataRow r)
{
dynamic e = new Dictionary();
int len = r.Table.Columns.Count;
for (int i = 0; i < len; i++)
e.Add(r.Table.Columns[i].ColumnName.ToUpper(), r.IsNull(i) ? null : r[i]);
return e;
}
/// Convert reader row into dynamic object.
/// Reader from which read.
/// Generated dynamic object.
public static dynamic RowToDynamic(this IDataReader r)
{
dynamic e = new DynamicExpando();
var d = e as IDictionary;
int c = r.FieldCount;
for (int i = 0; i < c; i++)
try
{
d.Add(r.GetName(i), r.IsDBNull(i) ? null : r[i]);
}
catch (ArgumentException argex)
{
throw new ArgumentException(
string.Format("Field '{0}' is defined more than once in a query.", r.GetName(i)), "Column name or alias", argex);
}
return e;
}
/// Convert reader row into dynamic object.
/// Reader from which read.
/// Generated dynamic object.
public static dynamic RowToExpando(this IDataReader r)
{
dynamic e = new ExpandoObject();
var d = e as IDictionary;
int c = r.FieldCount;
for (int i = 0; i < c; i++)
try
{
d.Add(r.GetName(i), r.IsDBNull(i) ? null : r[i]);
}
catch (ArgumentException argex)
{
throw new ArgumentException(
string.Format("Field '{0}' is defined more than once in a query.", r.GetName(i)), "Column name or alias", argex);
}
return e;
}
/// Turns the object into a Dictionary.
/// Object to convert.
/// Resulting dictionary.
public static IDictionary ToDictionary(this ExpandoObject o)
{
return (IDictionary)o;
}
/// Turns the object into a Dictionary.
/// Object to convert.
/// Resulting dictionary.
public static IDictionary ToDictionary(this DynamicExpando o)
{
return (IDictionary)o;
}
/// Turns the object into a Dictionary.
/// Object to convert.
/// Resulting dictionary.
public static IDictionary ToDictionary(this object o)
{
return o is IDictionary ?
(IDictionary)o :
(IDictionary)o.ToDynamic();
}
#endregion Dynamic extensions
#region Type extensions
/// Check if type is anonymous.
/// Type to test.
/// Returns true if type is anonymous.
public static bool IsAnonymous(this Type type)
{
// HACK: The only way to detect anonymous types right now.
return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
&& type.IsGenericType
&& (type.Name.Contains("AnonymousType") || type.Name.Contains("AnonType"))
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
}
/// Check if type implements IEnumerable<> interface.
/// Type to check.
/// Returns true if it does.
public static bool IsGenericEnumerable(this Type type)
{
return type.IsGenericType && type.GetInterfaces().Any(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
}
/// Check if type implements IEnumerable<> interface.
/// Type to check.
/// Returns true if it does.
public static bool IsNullableType(this Type type)
{
Type generic = type.IsGenericType ? type.GetGenericTypeDefinition() : null;
if (generic != null && generic.Equals(typeof(Nullable<>)) && type.IsClass)
return true;
return false;
}
/// Check if type is collection of any kind.
/// Type to check.
/// Returns true if it is.
public static bool IsCollection(this Type type)
{
if (!type.IsArray)
return type.IsGenericEnumerable();
return true;
}
/// Check if type is collection of value types like int.
/// Type to check.
/// Returns true if it is.
public static bool IsCollectionOfValueTypes(this Type type)
{
if (type.IsArray)
return type.GetElementType().IsValueType;
else
{
if (type.IsGenericType && type.GetInterfaces().Any(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
Type[] gt = type.GetGenericArguments();
return (gt.Length == 1) && gt[0].IsValueType;
}
}
return false;
}
/// Gets corresponding to the
/// provided .
/// The type to be converted.
/// Returns corresponding to the
/// provided .
public static DbType ToDbType(this Type t)
{
return TypeMap.TryGetNullable(t) ?? DbType.Object;
}
/// Gets corresponding to the
/// provided .
/// The type to be converted.
/// Returns corresponding to the
/// provided .
public static Type ToType(this DbType dbt)
{
foreach (var tdbt in TypeMap)
if (tdbt.Value == dbt)
return tdbt.Key;
return typeof(object);
}
#endregion Type extensions
#region IDictionary extensions
/// Gets the value associated with the specified key.
/// The type of keys in the dictionary.
/// The type of values in the dictionary.
/// Dictionary to probe.
/// The key whose value to get.
/// Nullable type containing value or null if key was not found.
public static TValue? TryGetNullable(this IDictionary dict, TKey key) where TValue : struct
{
TValue val;
if (key != null && dict.TryGetValue(key, out val))
return val;
return null;
}
/// Gets the value associated with the specified key.
/// The type of keys in the dictionary.
/// The type of values in the dictionary.
/// Dictionary to probe.
/// The key whose value to get.
/// Instance of object or null if not found.
public static TValue TryGetValue(this IDictionary dict, TKey key) where TValue : class
{
TValue val;
if (key != null && dict.TryGetValue(key, out val))
return val;
return default(TValue);
}
/// Adds element to dictionary and returns added value.
/// The type of keys in the dictionary.
/// The type of values in the dictionary.
/// Dictionary to which add value.
/// The key under which value value will be added.
/// Value to add.
/// Instance of object or null if not found.
public static TValue AddAndPassValue(this IDictionary dict, TKey key, TValue value)
{
dict.Add(key, value);
return value;
}
#endregion IDictionary extensions
#region IDataReader extensions
/// Gets the information corresponding
/// to the type of that would be returned from
/// .
/// The data reader.
/// The index of the field to find.
/// The information corresponding to the
/// type of that would be returned from
/// .
public static DbType GetFieldDbType(this IDataReader r, int i)
{
return TypeMap.TryGetNullable(r.GetFieldType(i)) ?? DbType.String;
}
#endregion IDataReader extensions
#region Mapper extensions
/// MapEnumerable object enumerator into specified type.
/// Type to which columnMap results.
/// Source enumerator.
/// Enumerator of specified type.
public static IEnumerable MapEnumerable(this IEnumerable enumerable)
{
var mapper = DynamicMapperCache.GetMapper();
if (mapper == null)
throw new InvalidOperationException("Type can't be mapped for unknown reason.");
foreach (var item in enumerable)
yield return (T)mapper.Create(item);
}
/// MapEnumerable object item into specified type.
/// Type to which columnMap results.
/// Source object.
/// Item of specified type.
public static T Map(this object item)
{
var mapper = DynamicMapperCache.GetMapper();
if (mapper == null)
throw new InvalidOperationException("Type can't be mapped for unknown reason.");
return (T)mapper.Create(item);
}
/// Fill object of specified type with data from source object.
/// Type to which columnMap results.
/// Item to which columnMap data.
/// Item from which extract data.
/// Filled item.
public static T Fill(this T item, object source)
{
var mapper = DynamicMapperCache.GetMapper();
if (mapper == null)
throw new InvalidOperationException("Type can't be mapped for unknown reason.");
mapper.Map(item, source);
return item;
}
/// MapEnumerable object enumerator into specified type.
/// Source enumerator.
/// Type to which columnMap results.
/// Enumerator of specified type.
public static IEnumerable MapEnumerable(this IEnumerable enumerable, Type type)
{
var mapper = DynamicMapperCache.GetMapper(type);
if (mapper == null)
throw new InvalidOperationException("Type can't be mapped for unknown reason.");
foreach (var item in enumerable)
yield return mapper.Create(item);
}
/// MapEnumerable object item into specified type.
/// Source object.
/// Type to which columnMap results.
/// Item of specified type.
public static object Map(this object item, Type type)
{
var mapper = DynamicMapperCache.GetMapper(type);
if (mapper == null)
throw new InvalidOperationException("Type can't be mapped for unknown reason.");
return mapper.Create(item);
}
/// Converts the elements of an
/// to the specified type.
/// The type to convert the elements of source to.
/// The that
/// contains the elements to be converted.
/// An enumerator that contains each element of
/// the source sequence converted to the specified type.
/// Thrown when
/// source is null.
/// An element in the
/// sequence cannot be cast to type T or enumerator
/// is not .
public static IEnumerable CastEnumerable(this object enumerator)
{
return (enumerator as System.Collections.IEnumerable).Cast();
}
#endregion Mapper extensions
#region TryParse extensions
#if !DYNAMORM_OMMIT_TRYPARSE
/// Generic try parse.
/// Type to parse to.
/// Value to parse.
/// Handler of a try parse method.
/// Returns true if conversion was successful.
public static T? TryParse(this string value, TryParseHandler handler) where T : struct
{
if (String.IsNullOrEmpty(value))
return null;
T result;
if (handler(value, out result))
return result;
return null;
}
/// Generic try parse with default value.
/// Type to parse to.
/// Value to parse.
/// Default value of a result.
/// Handler of a try parse method.
/// Returns true if conversion was successful.
public static T TryParseDefault(this string value, T defaultValue, TryParseHandler handler)
{
if (String.IsNullOrEmpty(value))
return defaultValue;
T result;
if (handler(value, out result))
return result;
return defaultValue;
}
/// Delegate fro try parse function of a type.
/// Type which implements this function.
/// Value to parse.
/// Resulting value.
/// Returns true if conversion was successful.
public delegate bool TryParseHandler(string value, out T result);
#endif
#endregion TryParse extensions
#region Coalesce - besicaly not an extensions
/// Select first not null value.
/// Type to return.
/// Values to check.
/// First not null or default value.
public static T Coalesce(params T[] vals) where T : class
{
return vals.FirstOrDefault(v => v != null);
}
/// Select first not null value.
/// Type to return.
/// Values to check.
/// First not null or default value.
public static T? CoalesceNullable(params T?[] vals) where T : struct
{
return vals.FirstOrDefault(v => v != null);
}
#endregion Coalesce - besicaly not an extensions
}
/// Dynamic query exception.
[Serializable]
public class DynamicQueryException : Exception, ISerializable
{
/// Initializes a new instance of the
/// class.
/// The command which failed.
public DynamicQueryException(IDbCommand command = null)
: base(string.Format("Error executing command.{0}{1}", Environment.NewLine, command != null ? command.DumpToString() : string.Empty))
{
}
/// Initializes a new instance of the
/// class.
/// The message.
/// The command which failed.
public DynamicQueryException(string message, IDbCommand command = null)
: base(string.Format("{0}{1}{2}", message, Environment.NewLine, command != null ? command.DumpToString() : string.Empty))
{
}
/// Initializes a new instance of the
/// class.
/// The inner exception.
/// The command which failed.
public DynamicQueryException(Exception innerException, IDbCommand command = null)
: base(string.Format("Error executing command.{0}{1}", Environment.NewLine, command != null ? command.DumpToString() : string.Empty), innerException)
{
}
/// Initializes a new instance of the
/// class.
/// The message.
/// The inner exception.
/// The command which failed.
public DynamicQueryException(string message, Exception innerException, IDbCommand command = null)
: base(string.Format("{0}{1}{2}", message, Environment.NewLine, command != null ? command.DumpToString() : string.Empty), innerException)
{
}
/// Initializes a new instance of the
/// class.
/// The
/// that holds the serialized object data about the exception being thrown.
/// The
/// that contains contextual information about the source or destination.
public DynamicQueryException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
/// When overridden in a derived class, sets the
///
/// with information about the exception.
/// The
/// that holds the serialized object data about the exception being thrown.
/// The
/// that contains contextual information about the source or destination.
///
///
///
///
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
/// Stores information about column from database schema.
public struct DynamicSchemaColumn
{
/// Gets or sets column name.
public string Name { get; set; }
/// Gets or sets column type.
public DbType Type { get; set; }
/// Gets or sets a value indicating whether column is a key.
public bool IsKey { get; set; }
/// Gets or sets a value indicating whether column should have unique value.
public bool IsUnique { get; set; }
/// Gets or sets column size.
public int Size { get; set; }
/// Gets or sets column precision.
public byte Precision { get; set; }
/// Gets or sets column scale.
public byte Scale { get; set; }
}
#if !DYNAMORM_OMMIT_OLDSYNTAX
/// Dynamic table is a simple ORM using dynamic objects.
///
/// Assume that we have a table representing Users class.
///
/// Let's take a look at Query possibilities. Assume we want
/// to get enumerator for all records in database, mapped to our class
/// instead of dynamic type we can use following syntax.
/// Approach first. Use dynamic Query method and just set type
/// then just cast it to user class. Remember that you must cast result
/// of Query to IEnumerable<object> . because from
/// point of view of runtime you are operating on object type.
/// (db.Table<User>().Query(type: typeof(User)) as IEnumerable<object>).Cast<User>();
/// Second approach is similar. We ask database using dynamic
/// Query method. The difference is that we use extension method of
/// IEnumerable<object> (to which we must cast to) to map
/// object.
/// (db.Table<User>().Query(columns: "*") as IEnumerable<object>).MapEnumerable<User>();
/// You can also use generic approach. But be careful this method is currently available thanks to framework hack.
/// (db.Table<User>().Query<User>() as IEnumerable<object>).Cast<User>()
/// Another approach uses existing methods, but still requires a
/// cast, because Query also returns dynamic object enumerator.
/// (db.Table<User>().Query().Execute() as IEnumerable<object>).MapEnumerable<User>();
///
/// Below you can find various invocations of dynamic and non dynamic
/// methods of this class. x variable is a class instance.
/// First various selects:
/// x.Count(columns: "id");
/// x.Count(last: new DynamicColumn
/// {
/// Operator = DynamicColumn.CompareOperator.In,
/// Value = new object[] { "Hendricks", "Goodwin", "Freeman" }.Take(3)
/// });
/// x.Count(last: new DynamicColumn
/// {
/// Operator = DynamicColumn.CompareOperator.In,
/// Value = new object[] { "Hendricks", "Goodwin", "Freeman" }
/// });
/// x.First(columns: "id").id;
/// x.Last(columns: "id").id;
/// x.Count(first: "Ori");
/// x.Min(columns: "id");
/// x.Max(columns: "id");
/// x.Avg(columns: "id");
/// x.Sum(columns: "id");
/// x.Scalar(columns: "first", id: 19);
/// x.Scalar(columns: "first:first:group_concat", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 });
/// x.Scalar(columns: "group_concat(first):first", id: new DynamicColumn { Operator = DynamicColumn.CompareOperator.Lt, Value = 20 });
/// var v = (x.Query(columns: "first,first:occurs:count", group: "first", order: ":desc:2") as IEnumerable<dynamic>).ToList();
/// x.Scalar(columns: @"length(""login""):len:avg");
/// x.Avg(columns: @"length(""email""):len");
/// x.Count(condition1:
/// new DynamicColumn()
/// {
/// ColumnName = "email",
/// Aggregate = "length",
/// Operator = DynamicColumn.CompareOperator.Gt,
/// Value = 27
/// });
/// var o = x.Single(columns: "id,first,last", id: 19);
/// x.Single(where: new DynamicColumn("id").Eq(100)).login;
/// x.Count(where: new DynamicColumn("id").Not(100));
/// x.Single(where: new DynamicColumn("login").Like("Hoyt.%")).id;
/// x.Count(where: new DynamicColumn("login").NotLike("Hoyt.%"));
/// x.Count(where: new DynamicColumn("id").Greater(100));
/// x.Count(where: new DynamicColumn("id").GreaterOrEqual(100));
/// x.Count(where: new DynamicColumn("id").Less(100));
/// x.Count(where: new DynamicColumn("id").LessOrEqual(100));
/// x.Count(where: new DynamicColumn("id").Between(75, 100));
/// x.Count(where: new DynamicColumn("id").In(75, 99, 100));
/// x.Count(where: new DynamicColumn("id").In(new[] { 75, 99, 100 }));
/// Inserts:
/// x.Insert(code: 201, first: "Juri", last: "Gagarin", email: "juri.gagarin@megacorp.com", quote: "bla, bla, bla");
/// x.Insert(values: new { code = 202, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" });
/// x.Insert(values: new Users
/// {
/// Id = u.Max(columns: "id") + 1,
/// Code = "203",
/// First = "Juri",
/// Last = "Gagarin",
/// Email = "juri.gagarin@megacorp.com",
/// Quote = "bla, bla, bla"
/// });
/// x.Insert(values: new users
/// {
/// id = u.Max(columns: "id") + 1,
/// code = "204",
/// first = "Juri",
/// last = "Gagarin",
/// email = "juri.gagarin@megacorp.com",
/// quote = "bla, bla, bla"
/// });
/// x.Update(id: 1, code: 201, first: "Juri", last: "Gagarin", email: "juri.gagarin@megacorp.com", quote: "bla, bla, bla");
/// x.Update(update: new { id = 2, code = 202, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" });
/// Updates:
/// x.Update(update: new Users
/// {
/// Id = 3,
/// Code = "203",
/// First = "Juri",
/// Last = "Gagarin",
/// Email = "juri.gagarin@megacorp.com",
/// Quote = "bla, bla, bla"
/// });
/// x.Update(update: new users
/// {
/// id = 4,
/// code = "204",
/// first = "Juri",
/// last = "Gagarin",
/// email = "juri.gagarin@megacorp.com",
/// quote = "bla, bla, bla"
/// });
/// x.Update(values: new { code = 205, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" }, where: new { id = 5 });
/// x.Update(values: new Users
/// {
/// Id = 6,
/// Code = "206",
/// First = "Juri",
/// Last = "Gagarin",
/// Email = "juri.gagarin@megacorp.com",
/// Quote = "bla, bla, bla"
/// }, id: 6);
/// x.Update(values: new users
/// {
/// id = 7,
/// code = "207",
/// first = "Juri",
/// last = "Gagarin",
/// email = "juri.gagarin@megacorp.com",
/// quote = "bla, bla, bla"
/// }, id: 7);
/// Delete:
/// x.Delete(code: 10);
/// x.Delete(delete: new { id = 11, code = 11, first = "Juri", last = "Gagarin", email = "juri.gagarin@megacorp.com", quote = "bla, bla, bla" });
/// x.Delete(delete: new Users
/// {
/// Id = 12,
/// Code = "12",
/// First = "Juri",
/// Last = "Gagarin",
/// Email = "juri.gagarin@megacorp.com",
/// Quote = "bla, bla, bla"
/// });
/// x.Delete(delete: new users
/// {
/// id = 13,
/// code = "13",
/// first = "Juri",
/// last = "Gagarin",
/// email = "juri.gagarin@megacorp.com",
/// quote = "bla, bla, bla"
/// });
/// x.Delete(where: new { id = 14, code = 14 });
///
public class DynamicTable : DynamicObject, IExtendedDisposable, ICloneable
{
private static HashSet _allowedCommands = new HashSet
{
"Insert", "Update", "Delete",
"Query", "Single", "Where",
"First", "Last", "Get",
"Count", "Sum", "Avg",
"Min", "Max", "Scalar"
};
/// Gets dynamic database.
internal DynamicDatabase Database { get; private set; }
/// Gets type of table (for coning and schema building).
internal Type TableType { get; private set; }
/// Gets name of table.
public virtual string TableName { get; private set; }
/// Gets name of owner.
public virtual string OwnerName { get; private set; }
/// Gets full name of table containing owner and table name.
public virtual string FullName
{
get
{
return string.IsNullOrEmpty(TableName) ? null : string.IsNullOrEmpty(OwnerName) ?
Database.DecorateName(TableName) :
string.Format("{0}.{1}", Database.DecorateName(OwnerName), Database.DecorateName(TableName));
}
}
/// Gets table schema.
/// If database doesn't support schema, only key columns are listed here.
public virtual Dictionary Schema { get; private set; }
private DynamicTable()
{
}
/// Initializes a new instance of the class.
/// Database and connection management.
/// Table name.
/// Owner of the table.
/// Override keys in schema.
public DynamicTable(DynamicDatabase database, string table = "", string owner = "", string[] keys = null)
{
IsDisposed = false;
Database = database;
TableName = Database.StripName(table);
OwnerName = Database.StripName(owner);
TableType = null;
BuildAndCacheSchema(keys);
}
/// Initializes a new instance of the class.
/// Database and connection management.
/// Type describing table.
/// Override keys in schema.
public DynamicTable(DynamicDatabase database, Type type, string[] keys = null)
{
if (type == null)
throw new ArgumentNullException("type", "Type can't be null.");
IsDisposed = false;
Database = database;
TableType = type;
var mapper = DynamicMapperCache.GetMapper(type);
if (mapper != null)
{
TableName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
type.Name : mapper.Table.Name;
OwnerName = mapper.Table == null || string.IsNullOrEmpty(mapper.Table.Name) ?
type.Name : mapper.Table.Name;
}
BuildAndCacheSchema(keys);
}
#region Schema
private void BuildAndCacheSchema(string[] keys)
{
Dictionary schema = null;
schema = Database.GetSchema(TableType) ??
Database.GetSchema(TableName, OwnerName);
#region Fill currrent table schema
if (keys == null && TableType != null)
{
var mapper = DynamicMapperCache.GetMapper(TableType);
if (mapper != null)
{
var k = mapper.ColumnsMap.Where(p => p.Value.Column != null && p.Value.Column.IsKey).Select(p => p.Key);
if (k.Count() > 0)
keys = k.ToArray();
}
}
if (schema != null)
{
if (keys == null)
Schema = new Dictionary(schema);
else
{
// TODO: Make this.... nicer
List ks = keys.Select(k => k.ToLower()).ToList();
Schema = schema.ToDictionary(k => k.Key, (v) =>
{
DynamicSchemaColumn dsc = v.Value;
dsc.IsKey = ks.Contains(v.Key);
return dsc;
});
}
}
#endregion Fill currrent table schema
#region Build ad-hock schema
if (keys != null && Schema == null)
{
Schema = keys.Select(k => k.ToLower()).ToList()
.ToDictionary(k => k, k => new DynamicSchemaColumn { Name = k, IsKey = true });
}
#endregion Build ad-hock schema
}
#endregion Schema
#region Basic Queries
/// Enumerate the reader and yield the result.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Enumerator of objects expanded from query.
public virtual IEnumerable Query(string sql, params object[] args)
{
return Database.Query(sql, args);
}
/// Enumerate the reader and yield the result.
/// Command builder.
/// Enumerator of objects expanded from query.
public virtual IEnumerable Query(IDynamicQueryBuilder builder)
{
return Database.Query(builder);
}
/// Create new .
/// New instance.
public virtual IDynamicSelectQueryBuilder Query()
{
var builder = new DynamicSelectQueryBuilder(this.Database);
var name = this.FullName;
if (!string.IsNullOrEmpty(name))
builder.From(x => name);
return builder;
}
/// Returns a single result.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Result of a query.
public virtual object Scalar(string sql, params object[] args)
{
return Database.Scalar(sql, args);
}
/// Returns a single result.
/// Command builder.
/// Result of a query.
public virtual object Scalar(IDynamicQueryBuilder builder)
{
return Database.Scalar(builder);
}
#if !DYNAMORM_OMMIT_GENERICEXECUTION && !DYNAMORM_OMMIT_TRYPARSE
/// Returns a single result.
/// What kind of result is expected.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Result of a query.
public virtual T ScalarAs(string sql, params object[] args)
{
return Database.ScalarAs(sql, args);
}
/// Returns a single result.
/// What kind of result is expected.
/// Command builder.
/// Default value.
/// Result of a query.
public virtual T ScalarAs(IDynamicQueryBuilder builder, T defaultValue = default(T))
{
return Database.ScalarAs(builder, defaultValue);
}
#endif
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Number of affected rows.
public virtual int Procedure(string procName)
{
return Database.Procedure(procName);
}
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Arguments (parameters) in form of expando object.
/// Number of affected rows.
public virtual int Procedure(string procName, params object[] args)
{
return Database.Procedure(procName, args);
}
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Arguments (parameters) in form of expando object.
/// Number of affected rows.
public virtual int Procedure(string procName, DynamicExpando args)
{
return Database.Procedure(procName, args);
}
/// Execute stored procedure.
/// Name of stored procedure to execute.
/// Arguments (parameters) in form of expando object.
/// Number of affected rows.
public virtual int Procedure(string procName, ExpandoObject args)
{
return Database.Procedure(procName, args);
}
/// Execute non query.
/// SQL query containing numbered parameters in format provided by
/// methods. Also names should be formatted with
/// method.
/// Arguments (parameters).
/// Number of affected rows.
public virtual int Execute(string sql, params object[] args)
{
return Database.Execute(sql, args);
}
/// Execute non query.
/// Command builder.
/// Number of affected rows.
public virtual int Execute(IDynamicQueryBuilder builder)
{
return Database.Execute(builder);
}
/// Execute non query.
/// Command builders.
/// Number of affected rows.
public virtual int Execute(IDynamicQueryBuilder[] builders)
{
return Database.Execute(builders);
}
#endregion Basic Queries
#region Insert
/// Create new .
/// New instance.
public dynamic Insert()
{
return new DynamicProxy(new DynamicInsertQueryBuilder(this.Database, this.FullName));
}
/// Adds a record to the database. You can pass in an Anonymous object, an ,
/// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString.
/// Anonymous object, an , a regular old POCO, or a NameValueCollection
/// from a Request.Form or Request.QueryString, containing fields to update.
/// Number of updated rows.
public virtual int Insert(object o)
{
return Insert()
.Insert(o)
.Execute();
}
#endregion Insert
#region Update
/// Create new .
/// New instance.
public dynamic Update()
{
return new DynamicProxy(new DynamicUpdateQueryBuilder(this.Database, this.FullName));
}
/// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject,
/// a regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString.
/// Anonymous object, an ExpandoObject, a regular old POCO, or a NameValueCollection
/// from a Request.Form or Request.QueryString, containing fields to update.
/// Anonymous object, an , a regular old POCO, or a NameValueCollection
/// from a Request.Form or Request.QueryString, containing fields with conditions.
/// Number of updated rows.
public virtual int Update(object o, object key)
{
return Update()
.Values(o)
.Where(key)
.Execute();
}
/// Updates a record in the database using schema. You can pass in an Anonymous object, an ExpandoObject,
/// a regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString.
/// Anonymous object, an , a regular old POCO, or a NameValueCollection
/// from a Request.Form or Request.QueryString, containing fields to update and conditions.
/// Number of updated rows.
public virtual int Update(object o)
{
return Update()
.Update(o)
.Execute();
}
#endregion Update
#region Delete
/// Create new .
/// New instance.
public dynamic Delete()
{
return new DynamicProxy(new DynamicDeleteQueryBuilder(this.Database, this.FullName));
}
/// Removes a record from the database. You can pass in an Anonymous object, an ,
/// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString.
/// Anonymous object, an , a regular old POCO, or a NameValueCollection
/// from a Request.Form or Request.QueryString, containing fields with where conditions.
/// If true use schema to determine key columns and ignore those which
/// aren't keys.
/// Number of updated rows.
public virtual int Delete(object o, bool schema = true)
{
return Delete()
.Where(o, schema)
.Execute();
}
#endregion Delete
#region Universal Dynamic Invoker
/// This is where the magic begins.
/// Binder to invoke.
/// Binder arguments.
/// Binder invoke result.
/// Returns true if invoke was performed.
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
// parse the method
var info = binder.CallInfo;
// Get generic types
var types = binder.GetGenericTypeArguments();
// accepting named args only... SKEET!
if (info.ArgumentNames.Count != args.Length)
throw new InvalidOperationException("Please use named arguments for this type of query - the column name, orderby, columns, etc");
var op = binder.Name;
// Avoid strange things
if (!_allowedCommands.Contains(op))
throw new InvalidOperationException(string.Format("Dynamic method '{0}' is not supported.", op));
switch (op)
{
case "Insert":
result = DynamicInsert(args, info, types);
break;
case "Update":
result = DynamicUpdate(args, info, types);
break;
case "Delete":
result = DynamicDelete(args, info, types);
break;
default:
result = DynamicQuery(args, info, op, types);
break;
}
return true;
}
private object DynamicInsert(object[] args, CallInfo info, IList types)
{
var builder = new DynamicInsertQueryBuilder(this.Database);
if (types != null && types.Count == 1)
HandleTypeArgument(null, info, ref types, builder, 0);
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema);
// loop the named args - see if we have order, columns and constraints
if (info.ArgumentNames.Count > 0)
{
for (int i = 0; i < args.Length; i++)
{
var fullName = info.ArgumentNames[i];
var name = fullName.ToLower();
switch (name)
{
case "table":
if (args[i] is string)
builder.Table(args[i].ToString());
else goto default;
break;
case "values":
builder.Insert(args[i]);
break;
case "type":
if (types == null || types.Count == 0)
HandleTypeArgument(args, info, ref types, builder, i);
else goto default;
break;
default:
builder.Insert(fullName, args[i]);
break;
}
}
}
// Execute
return Execute(builder);
}
private object DynamicUpdate(object[] args, CallInfo info, IList types)
{
var builder = new DynamicUpdateQueryBuilder(this.Database);
if (types != null && types.Count == 1)
HandleTypeArgument(null, info, ref types, builder, 0);
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema);
// loop the named args - see if we have order, columns and constraints
if (info.ArgumentNames.Count > 0)
{
for (int i = 0; i < args.Length; i++)
{
var fullName = info.ArgumentNames[i];
var name = fullName.ToLower();
switch (name)
{
case "table":
if (args[i] is string)
builder.Table(args[i].ToString());
else goto default;
break;
case "update":
builder.Update(args[i]);
break;
case "values":
builder.Values(args[i]);
break;
case "where":
builder.Where(args[i]);
break;
case "type":
if (types == null || types.Count == 0)
HandleTypeArgument(args, info, ref types, builder, i);
else goto default;
break;
default:
builder.Update(fullName, args[i]);
break;
}
}
}
// Execute
return Execute(builder);
}
private object DynamicDelete(object[] args, CallInfo info, IList types)
{
var builder = new DynamicDeleteQueryBuilder(this.Database);
if (types != null && types.Count == 1)
HandleTypeArgument(null, info, ref types, builder, 0);
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
builder.Table(string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName), this.Schema);
// loop the named args - see if we have order, columns and constraints
if (info.ArgumentNames.Count > 0)
{
for (int i = 0; i < args.Length; i++)
{
var fullName = info.ArgumentNames[i];
var name = fullName.ToLower();
switch (name)
{
case "table":
if (args[i] is string)
builder.Table(args[i].ToString());
else goto default;
break;
case "where":
builder.Where(args[i], false);
break;
case "delete":
builder.Where(args[i], true);
break;
case "type":
if (types == null || types.Count == 0)
HandleTypeArgument(args, info, ref types, builder, i);
else goto default;
break;
default:
builder.Where(fullName, args[i]);
break;
}
}
}
// Execute
return Execute(builder);
}
private object DynamicQuery(object[] args, CallInfo info, string op, IList types)
{
object result;
var builder = new DynamicSelectQueryBuilder(this.Database);
if (types != null && types.Count == 1)
HandleTypeArgument(null, info, ref types, builder, 0);
if (!string.IsNullOrEmpty(this.TableName) && builder.Tables.Count == 0)
builder.From(x => string.IsNullOrEmpty(this.OwnerName) ? this.TableName : string.Format("{0}.{1}", this.OwnerName, this.TableName));
// loop the named args - see if we have order, columns and constraints
if (info.ArgumentNames.Count > 0)
{
for (int i = 0; i < args.Length; i++)
{
var fullName = info.ArgumentNames[i];
var name = fullName.ToLower();
// TODO: Make this nicer
switch (name)
{
case "order":
if (args[i] is string)
builder.OrderByColumn(((string)args[i]).Split(','));
else if (args[i] is string[])
builder.OrderByColumn(args[i] as string);
else if (args[i] is DynamicColumn[])
builder.OrderByColumn((DynamicColumn[])args[i]);
else if (args[i] is DynamicColumn)
builder.OrderByColumn((DynamicColumn)args[i]);
else goto default;
break;
case "group":
if (args[i] is string)
builder.GroupByColumn(((string)args[i]).Split(','));
else if (args[i] is string[])
builder.GroupByColumn(args[i] as string);
else if (args[i] is DynamicColumn[])
builder.GroupByColumn((DynamicColumn[])args[i]);
else if (args[i] is DynamicColumn)
builder.GroupByColumn((DynamicColumn)args[i]);
else goto default;
break;
case "columns":
{
var agregate = (op == "Sum" || op == "Max" || op == "Min" || op == "Avg" || op == "Count") ?
op.ToUpper() : null;
if (args[i] is string || args[i] is string[])
builder.SelectColumn((args[i] as String).NullOr(s => s.Split(','), args[i] as String[])
.Select(c =>
{
var col = DynamicColumn.ParseSelectColumn(c);
if (string.IsNullOrEmpty(col.Aggregate))
col.Aggregate = agregate;
return col;
}).ToArray());
else if (args[i] is DynamicColumn || args[i] is DynamicColumn[])
builder.SelectColumn((args[i] as DynamicColumn).NullOr(c => new DynamicColumn[] { c }, args[i] as DynamicColumn[])
.Select(c =>
{
if (string.IsNullOrEmpty(c.Aggregate))
c.Aggregate = agregate;
return c;
}).ToArray());
else goto default;
}
break;
case "where":
builder.Where(args[i]);
break;
case "table":
if (args[i] is string)
builder.From(x => args[i].ToString());
else goto default;
break;
case "type":
if (types == null || types.Count == 0)
HandleTypeArgument(args, info, ref types, builder, i);
else goto default;
break;
default:
builder.Where(fullName, args[i]);
break;
}
}
}
if (op == "Count" && !builder.HasSelectColumns)
{
result = Scalar(builder.Select(x => x.Count()));
if (result is long)
result = (int)(long)result;
}
else if (op == "Sum" || op == "Max" ||
op == "Min" || op == "Avg" || op == "Count")
{
if (!builder.HasSelectColumns)
throw new InvalidOperationException("You must select one column to agregate.");
result = Scalar(builder);
if (op == "Count" && result is long)
result = (int)(long)result;
else if (result == DBNull.Value)
result = null;
}
else
{
// build the SQL
var justOne = op == "First" || op == "Last" || op == "Get" || op == "Single";
// Be sure to sort by DESC on selected columns
/*if (op == "Last")
{
if (builder.Order.Count > 0)
foreach (var o in builder.Order)
o.Order = o.Order == DynamicColumn.SortOrder.Desc ?
DynamicColumn.SortOrder.Asc : DynamicColumn.SortOrder.Desc;
}*/
if (justOne && !(op == "Last"))
{
if ((Database.Options & DynamicDatabaseOptions.SupportLimitOffset) == DynamicDatabaseOptions.SupportLimitOffset)
builder.Limit(1);
else if ((Database.Options & DynamicDatabaseOptions.SupportTop) == DynamicDatabaseOptions.SupportTop)
builder.Top(1);
}
if (op == "Scalar")
{
if (!builder.HasSelectColumns)
throw new InvalidOperationException("You must select one column in scalar statement.");
result = Scalar(builder);
}
else
{
if (justOne)
{
if (op == "Last")
result = Query(builder).LastOrDefault(); // Last record fallback
else
result = Query(builder).FirstOrDefault(); // return a single record
}
else
result = Query(builder); // return lots
// MapEnumerable to specified result (still needs to be casted after that)
if (types != null)
{
if (types.Count == 1)
result = justOne ?
result.Map(types[0]) :
((IEnumerable)result).MapEnumerable(types[0]);
// TODO: Dictionaries
}
}
}
return result;
}
private void HandleTypeArgument