/*
* DynamORM - Dynamic Object-Relational Mapping library.
* Copyright (c) 2012-2015, 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.Concurrent;
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 System.Text.RegularExpressions;
using DynamORM.Builders;
using DynamORM.Builders.Extensions;
using DynamORM.Builders.Implementation;
using DynamORM.Helpers;
using DynamORM.Helpers.Dynamics;
using DynamORM.Mapper;
using DynamORM.Validation;
[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
{
/// Cache data reader in memory.
public class DynamicCachedReader : DynamicObject, IDataReader
{
#region Constructor and Data
private DataTable _schema;
private int _fields;
private int _rows;
private int _position;
private int _cachePos;
private IList _names;
private IDictionary _ordinals;
private IList _types;
private IList _cache;
private DynamicCachedReader()
{
}
/// Initializes a new instance of the class.
/// Reader to cache.
/// The offset row.
/// The limit to number of tows. -1 is no limit.
/// The progress delegate.
public DynamicCachedReader(IDataReader reader, int offset = 0, int limit = -1, Func progress = null)
{
InitDataReader(reader, offset, limit, progress);
}
#endregion Constructor and Data
#region Helpers
/// Create data reader from enumerable.
/// Type of enumerated objects.
/// List of objects.
/// Instance of containing objects data.
public static DynamicCachedReader FromEnumerable(IEnumerable objects)
{
var mapper = DynamicMapperCache.GetMapper();
if (mapper == null)
throw new InvalidCastException(string.Format("Object type '{0}' can't be mapped.", typeof(T).FullName));
var r = new DynamicCachedReader();
r.Init(mapper.ColumnsMap.Count + 1);
r.CreateSchemaTable(mapper);
r.FillFromEnumerable(objects, mapper);
r.IsClosed = false;
r._position = -1;
r._cachePos = -1;
return r;
}
/// Create data reader from enumerable.
/// Type of enumerated objects.
/// List of objects.
/// Instance of containing objects data.
public static DynamicCachedReader FromEnumerable(Type elementType, IEnumerable objects)
{
var mapper = DynamicMapperCache.GetMapper(elementType);
if (mapper == null)
throw new InvalidCastException(string.Format("Object type '{0}' can't be mapped.", elementType.FullName));
var r = new DynamicCachedReader();
r.Init(mapper.ColumnsMap.Count + 1);
r.CreateSchemaTable(mapper);
r.FillFromEnumerable(elementType, objects, mapper);
r.IsClosed = false;
r._position = -1;
r._cachePos = -1;
return r;
}
private void InitDataReader(IDataReader reader, int offset = 0, int limit = -1, Func progress = null)
{
_schema = reader.GetSchemaTable();
RecordsAffected = reader.RecordsAffected;
Init(reader.FieldCount);
int i = 0;
for (i = 0; i < _fields; i++)
{
_names.Add(reader.GetName(i));
_types.Add(reader.GetFieldType(i));
if (!_ordinals.ContainsKey(reader.GetName(i).ToUpper()))
_ordinals.Add(reader.GetName(i).ToUpper(), i);
}
int current = 0;
while (reader.Read())
{
if (current < offset)
{
current++;
continue;
}
for (i = 0; i < _fields; i++)
_cache.Add(reader[i]);
_rows++;
current++;
if (limit >= 0 && _rows >= limit)
break;
if (progress != null && !progress(this, _rows))
break;
}
IsClosed = false;
_position = -1;
_cachePos = -1;
if (progress != null)
progress(this, _rows);
reader.Close();
}
private void FillFromEnumerable(IEnumerable objects, DynamicTypeMap mapper)
{
foreach (var elem in objects)
{
foreach (var col in mapper.ColumnsMap)
{
object val = null;
if (col.Value.Get != null)
val = col.Value.Get(elem);
_cache.Add(val);
}
_cache.Add(elem);
_rows++;
}
}
private void FillFromEnumerable(Type elementType, IEnumerable objects, DynamicTypeMap mapper)
{
foreach (var elem in objects)
{
foreach (var col in mapper.ColumnsMap)
{
object val = null;
if (col.Value.Get != null)
val = col.Value.Get(elem);
_cache.Add(val);
}
_cache.Add(elem);
_rows++;
}
}
private void CreateSchemaTable(DynamicTypeMap mapper)
{
_schema = new DataTable("DYNAMIC");
_schema.Columns.Add(new DataColumn("ColumnName", typeof(string)));
_schema.Columns.Add(new DataColumn("ColumnOrdinal", typeof(int)));
_schema.Columns.Add(new DataColumn("ColumnSize", typeof(int)));
_schema.Columns.Add(new DataColumn("NumericPrecision", typeof(short)));
_schema.Columns.Add(new DataColumn("NumericScale", typeof(short)));
_schema.Columns.Add(new DataColumn("DataType", typeof(Type)));
_schema.Columns.Add(new DataColumn("ProviderType", typeof(int)));
_schema.Columns.Add(new DataColumn("NativeType", typeof(int)));
_schema.Columns.Add(new DataColumn("AllowDBNull", typeof(bool)));
_schema.Columns.Add(new DataColumn("IsUnique", typeof(bool)));
_schema.Columns.Add(new DataColumn("IsKey", typeof(bool)));
_schema.Columns.Add(new DataColumn("IsAutoIncrement", typeof(bool)));
int ordinal = 0;
DataRow dr = null;
foreach (var column in mapper.ColumnsMap)
{
dr = _schema.NewRow();
dr[0] = column.Value.Column.NullOr(x => x.Name ?? column.Value.Name, column.Value.Name);
dr[1] = ordinal;
dr[2] = column.Value.Column.NullOr(x => x.Size ?? int.MaxValue, int.MaxValue);
dr[3] = column.Value.Column.NullOr(x => x.Precision ?? 0, 0);
dr[4] = column.Value.Column.NullOr(x => x.Scale ?? 0, 0);
dr[5] = column.Value.Column.NullOr(x => x.Type.HasValue ? x.Type.Value.ToType() : column.Value.Type, column.Value.Type);
dr[6] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType());
dr[7] = column.Value.Column.NullOr(x => x.Type ?? column.Value.Type.ToDbType(), column.Value.Type.ToDbType());
dr[8] = column.Value.Column.NullOr(x => x.IsKey, false) ? true : column.Value.Column.NullOr(x => x.AllowNull, true);
dr[9] = column.Value.Column.NullOr(x => x.IsUnique, false);
dr[10] = column.Value.Column.NullOr(x => x.IsKey, false);
dr[11] = false;
_schema.Rows.Add(dr);
_names.Add(dr[0].ToString());
_ordinals.Add(dr[0].ToString().ToUpper(), ordinal++);
_types.Add((Type)dr[5]);
dr.AcceptChanges();
}
dr = _schema.NewRow();
dr[0] = "#O";
dr[1] = ordinal;
dr[2] = int.MaxValue;
dr[3] = 0;
dr[4] = 0;
dr[5] = mapper.Type;
dr[6] = DbType.Object;
dr[7] = DbType.Object;
dr[8] = true;
dr[9] = false;
dr[10] = false;
dr[11] = false;
_schema.Rows.Add(dr);
_names.Add("#O");
_ordinals.Add("#O".ToUpper(), ordinal++);
_types.Add(mapper.Type);
dr.AcceptChanges();
}
private void Init(int fieldCount)
{
_rows = 0;
_fields = fieldCount;
_names = new List(_fields);
_ordinals = new Dictionary(_fields);
_types = new List(_fields);
_cache = new List(_fields * 100);
}
/// Sets the current position in reader.
/// The position.
public void SetPosition(int pos)
{
if (pos >= -1 && pos < _rows)
{
_position = pos;
_cachePos = _position * _fields;
}
else
throw new IndexOutOfRangeException();
}
#endregion Helpers
#region IDataReader Members
/// Closes the System.Data.IDataReader Object.
public void Close()
{
IsClosed = true;
_position = _rows;
_cachePos = -1;
}
/// Gets a value indicating the depth of nesting for the current row.
/// This implementation use this field to indicate row count.
public int Depth
{
get { return _rows; }
}
/// Returns a System.Data.DataTable that describes the column metadata of the
/// System.Data.IDataReader. A System.Data.DataTable that describes
/// the column metadata.
/// The System.Data.IDataReader is closed.
public DataTable GetSchemaTable()
{
return _schema;
}
/// Gets a value indicating whether the data reader is closed.
public bool IsClosed { get; private set; }
/// Advances the data reader to the next result, when reading the results of batch SQL statements.
/// Returns true if there are more rows; otherwise, false.
public bool NextResult()
{
_cachePos = (++_position) * _fields;
return _position < _rows;
}
/// Advances the System.Data.IDataReader to the next record.
/// Returns true if there are more rows; otherwise, false.
public bool Read()
{
_cachePos = (++_position) * _fields;
return _position < _rows;
}
/// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
/// The number of rows changed, inserted, or deleted; 0 if no rows were affected or the statement
/// failed; and -1 for SELECT statements.
public int RecordsAffected { get; private set; }
#endregion IDataReader Members
#region IDisposable Members
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
public void Dispose()
{
_names.Clear();
_types.Clear();
_cache.Clear();
_schema.Dispose();
}
#endregion IDisposable Members
#region IDataRecord Members
/// Gets the number of columns in the current row.
/// When not positioned in a valid record set, 0; otherwise, the number of columns in the current record. The default is -1.
public int FieldCount { get { return _fields; } }
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public bool GetBoolean(int i)
{
return (bool)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public byte GetByte(int i)
{
return (byte)_cache[_cachePos + i];
}
/// Reads a stream of bytes from the specified column offset into the buffer
/// as an array, starting at the given buffer offset.
/// The index of the field to find.
/// The index within the field from which to start the read operation.
/// The buffer into which to read the stream of bytes.
/// The index for buffer to start the read operation.
/// The number of bytes to read.
/// The actual number of bytes read.
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
using (MemoryStream ms = new MemoryStream((byte[])_cache[_cachePos + i]))
return ms.Read(buffer, bufferoffset, length);
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public char GetChar(int i)
{
return (char)_cache[_cachePos + i];
}
/// Reads a stream of characters from the specified column offset into the buffer
/// as an array, starting at the given buffer offset.
/// The zero-based column ordinal.
/// The index within the row from which to start the read operation.
/// The buffer into which to read the stream of bytes.
/// The index for buffer to start the read operation.
/// The number of bytes to read.
/// The actual number of characters read.
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
using (MemoryStream ms = new MemoryStream((byte[])_cache[_cachePos + i]))
{
byte[] buff = new byte[buffer.Length];
long ret = ms.Read(buff, bufferoffset, length);
for (int n = bufferoffset; n < ret; n++)
buffer[n] = (char)buff[n];
return ret;
}
}
/// Returns an System.Data.IDataReader for the specified column ordinal.
/// The index of the field to find.
/// An System.Data.IDataReader.
public IDataReader GetData(int i)
{
return null;
}
/// Gets the data type information for the specified field.
/// The index of the field to find.
/// The data type information for the specified field.
public string GetDataTypeName(int i)
{
return _types[i].Name;
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public DateTime GetDateTime(int i)
{
return (DateTime)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public decimal GetDecimal(int i)
{
return (decimal)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public double GetDouble(int i)
{
return (double)_cache[_cachePos + i];
}
/// Gets the System.Type information corresponding to the type of System.Object
/// that would be returned from System.Data.IDataRecord.GetValue(System.Int32).
/// The index of the field to find.
/// The System.Type information corresponding to the type of System.Object that
/// would be returned from System.Data.IDataRecord.GetValue(System.Int32).
public Type GetFieldType(int i)
{
return _types[i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public float GetFloat(int i)
{
return (float)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public Guid GetGuid(int i)
{
return (Guid)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public short GetInt16(int i)
{
return (short)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public int GetInt32(int i)
{
return (int)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public long GetInt64(int i)
{
return (long)_cache[_cachePos + i];
}
/// Gets the name for the field to find.
/// The index of the field to find.
/// The name of the field or the empty string (""), if there is no value to return.
public string GetName(int i)
{
return _names[i];
}
/// Return the index of the named field.
/// The name of the field to find.
/// The index of the named field.
public int GetOrdinal(string name)
{
if (_ordinals.ContainsKey(name.ToUpper()))
return _ordinals[name.ToUpper()];
return -1;
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public string GetString(int i)
{
return (string)_cache[_cachePos + i];
}
/// Return the value of the specified field.
/// The index of the field to find.
/// Field value upon return.
public object GetValue(int i)
{
return _cache[_cachePos + i];
}
/// Gets all the attribute fields in the collection for the current record.
/// An array of System.Object to copy the attribute fields into.
/// The number of instances of System.Object in the array.
public int GetValues(object[] values)
{
for (int i = 0; i < _fields; i++)
values[i] = _cache[_cachePos + i];
return _fields;
}
/// Return whether the specified field is set to null.
/// The index of the field to find.
/// Returns true if the specified field is set to null; otherwise, false.
public bool IsDBNull(int i)
{
return _cache[_cachePos + i] == null || _cache[_cachePos + i] == DBNull.Value;
}
/// Gets or sets specified value in current record.
/// Name of column.
/// Value of specified column.
public object this[string name]
{
get
{
if (_ordinals.ContainsKey(name.ToUpper()))
return _cache[_cachePos + _ordinals[name.ToUpper()]];
throw new IndexOutOfRangeException(String.Format("Field '{0}' not found.", name));
}
}
/// Gets or sets specified value in current record.
/// The index of the field to find.
/// Value of specified column.
public object this[int i]
{
get { return _cache[_cachePos + i]; }
}
#endregion IDataRecord Members
}
/// 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
string[] 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
string[] 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)
{
List pool = _db.CommandsPool.TryGetValue(_con.Connection);
if (pool != null && pool.Contains(this))
pool.Remove(this);
}
IsDisposed = true;
if (_command != null)
{
_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 DynamicProcedureInvoker _proc;
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 the procedures invoker.
public dynamic Procedures
{
get
{
if (_proc == null)
{
if ((Options & DynamicDatabaseOptions.SupportStoredProcedures) != DynamicDatabaseOptions.SupportStoredProcedures)
throw new InvalidOperationException("Database connection desn't support stored procedures.");
_proc = new DynamicProcedureInvoker(this);
}
return _proc;
}
}
/// Gets or sets a value indicating whether
/// dump commands to console or not.
public bool DumpCommands { get; set; }
/// Gets or sets the dump command delegate.
/// The dump command delegate.
public Action DumpCommandDelegate { 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)
{
// Try to find correct provider if possible
Type t = connection.GetType();
if (t == typeof(System.Data.SqlClient.SqlConnection))
_provider = System.Data.SqlClient.SqlClientFactory.Instance;
else if (t == typeof(System.Data.Odbc.OdbcConnection))
_provider = System.Data.Odbc.OdbcFactory.Instance;
else if (t == typeof(System.Data.OleDb.OleDbConnection))
_provider = System.Data.OleDb.OleDbFactory.Instance;
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 (KeyValuePair 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.
/// Table alias.
/// This instance to permit chaining.
public virtual IDynamicSelectQueryBuilder From(string alias)
{
return new DynamicSelectQueryBuilder(this).From(x => x(typeof(T)).As(alias));
}
/// 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
{
return Insert(typeof(T), e);
}
/// 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(Type t, IEnumerable e)
{
int affected = 0;
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(t);
if (mapper != null)
{
using (IDbConnection con = Open())
using (IDbTransaction tra = con.BeginTransaction())
using (IDbCommand cmd = con.CreateCommand())
{
try
{
Dictionary parameters = new Dictionary();
if (!string.IsNullOrEmpty(mapper.InsertCommandText))
{
cmd.CommandText = mapper.InsertCommandText;
foreach (DynamicPropertyInvoker col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.InsertCommandParameter != null)
.OrderBy(di => di.InsertCommandParameter.Ordinal))
{
IDbDataParameter para = cmd.CreateParameter();
para.ParameterName = col.InsertCommandParameter.Name;
para.DbType = col.InsertCommandParameter.Type;
cmd.Parameters.Add(para);
parameters[para] = col;
}
}
else
PrepareBatchInsert(t, mapper, cmd, parameters);
foreach (var o in e)
{
foreach (KeyValuePair 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;
StringBuilder 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
{
return Update(typeof(T), e);
}
/// 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(Type t, IEnumerable e)
{
int affected = 0;
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(t);
if (mapper != null)
{
using (IDbConnection con = Open())
using (IDbTransaction tra = con.BeginTransaction())
using (IDbCommand cmd = con.CreateCommand())
{
try
{
Dictionary parameters = new Dictionary();
if (!string.IsNullOrEmpty(mapper.UpdateCommandText))
{
cmd.CommandText = mapper.UpdateCommandText;
foreach (DynamicPropertyInvoker col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.UpdateCommandParameter != null)
.OrderBy(di => di.UpdateCommandParameter.Ordinal))
{
IDbDataParameter para = cmd.CreateParameter();
para.ParameterName = col.UpdateCommandParameter.Name;
para.DbType = col.UpdateCommandParameter.Type;
cmd.Parameters.Add(para);
parameters[para] = col;
}
}
else
PrepareBatchUpdate(t, mapper, cmd, parameters);
foreach (var o in e)
{
foreach (KeyValuePair 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;
StringBuilder 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
{
return UpdateOrInsert(typeof(T), e);
}
/// 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(Type t, IEnumerable e)
{
int affected = 0;
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(t);
if (mapper != null)
{
using (IDbConnection con = Open())
using (IDbTransaction tra = con.BeginTransaction())
using (IDbCommand cmdUp = con.CreateCommand())
using (IDbCommand cmdIn = con.CreateCommand())
{
try
{
#region Update
Dictionary parametersUp = new Dictionary();
if (!string.IsNullOrEmpty(mapper.UpdateCommandText))
{
cmdUp.CommandText = mapper.UpdateCommandText;
foreach (DynamicPropertyInvoker col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.UpdateCommandParameter != null)
.OrderBy(di => di.UpdateCommandParameter.Ordinal))
{
IDbDataParameter para = cmdUp.CreateParameter();
para.ParameterName = col.UpdateCommandParameter.Name;
para.DbType = col.UpdateCommandParameter.Type;
cmdUp.Parameters.Add(para);
parametersUp[para] = col;
}
}
else
PrepareBatchUpdate(t, mapper, cmdUp, parametersUp);
#endregion Update
#region Insert
Dictionary parametersIn = new Dictionary();
if (!string.IsNullOrEmpty(mapper.InsertCommandText))
{
cmdIn.CommandText = mapper.InsertCommandText;
foreach (DynamicPropertyInvoker col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.InsertCommandParameter != null)
.OrderBy(di => di.InsertCommandParameter.Ordinal))
{
IDbDataParameter para = cmdIn.CreateParameter();
para.ParameterName = col.InsertCommandParameter.Name;
para.DbType = col.InsertCommandParameter.Type;
cmdIn.Parameters.Add(para);
parametersIn[para] = col;
}
}
else
PrepareBatchInsert(t, mapper, cmdIn, parametersIn);
#endregion Insert
foreach (var o in e)
{
foreach (KeyValuePair m in parametersUp)
m.Key.Value = m.Value.Get(o);
int a = cmdUp.ExecuteNonQuery();
if (a == 0)
{
foreach (KeyValuePair 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;
StringBuilder 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));
}
/// Adds to the DELETE FROM clause using .
/// Type which can be represented in database.
/// This instance to permit chaining.
public virtual IDynamicDeleteQueryBuilder Delete(Type t)
{
return new DynamicDeleteQueryBuilder(this).Table(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
{
return Delete(typeof(T), e);
}
/// 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(Type t, IEnumerable e)
{
int affected = 0;
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(t);
if (mapper != null)
{
using (IDbConnection con = Open())
using (IDbTransaction tra = con.BeginTransaction())
using (IDbCommand cmd = con.CreateCommand())
{
try
{
Dictionary parameters = new Dictionary();
if (!string.IsNullOrEmpty(mapper.DeleteCommandText))
{
cmd.CommandText = mapper.DeleteCommandText;
foreach (DynamicPropertyInvoker col in mapper.ColumnsMap.Values
.Where(di => !di.Ignore && di.DeleteCommandParameter != null)
.OrderBy(di => di.DeleteCommandParameter.Ordinal))
{
IDbDataParameter para = cmd.CreateParameter();
para.ParameterName = col.DeleteCommandParameter.Name;
para.DbType = col.DeleteCommandParameter.Type;
cmd.Parameters.Add(para);
parameters[para] = col;
}
}
else
PrepareBatchDelete(t, mapper, cmd, parameters);
foreach (var o in e)
{
foreach (KeyValuePair 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;
StringBuilder problematicCommand = new StringBuilder();
cmd.Dump(problematicCommand);
throw new InvalidOperationException(problematicCommand.ToString(), ex);
}
}
}
return affected;
}
private void PrepareBatchInsert(DynamicTypeMap mapper, IDbCommand cmd, Dictionary parameters)
{
PrepareBatchInsert(typeof(T), mapper, cmd, parameters);
}
private void PrepareBatchInsert(Type t, DynamicTypeMap mapper, IDbCommand cmd, Dictionary parameters)
{
DynamicPropertyInvoker currentprop = null;
Dictionary temp = new Dictionary();
Dictionary schema = this.GetSchema(t);
int ord = 0;
IDynamicInsertQueryBuilder ib = Insert(t)
.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 (KeyValuePair prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
string 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)
{
PrepareBatchUpdate(typeof(T), mapper, cmd, parameters);
}
private void PrepareBatchUpdate(Type t, DynamicTypeMap mapper, IDbCommand cmd, Dictionary parameters)
{
DynamicPropertyInvoker currentprop = null;
Dictionary temp = new Dictionary();
Dictionary schema = this.GetSchema(t);
int ord = 0;
IDynamicUpdateQueryBuilder ib = Update(t)
.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 (KeyValuePair prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
string col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore)
continue;
if (currentprop.Get != null)
{
DynamicSchemaColumn? 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)
{
PrepareBatchDelete(typeof(T), mapper, cmd, parameters);
}
private void PrepareBatchDelete(Type t, DynamicTypeMap mapper, IDbCommand cmd, Dictionary parameters)
{
DynamicPropertyInvoker currentprop = null;
Dictionary temp = new Dictionary();
Dictionary schema = this.GetSchema(t);
int ord = 0;
IDynamicDeleteQueryBuilder ib = Delete(t)
.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 (KeyValuePair prop in mapper.PropertyMap)
if (!mapper.Ignored.Contains(prop.Key))
{
string col = mapper.PropertyMap.TryGetValue(prop.Key) ?? prop.Key;
currentprop = mapper.ColumnsMap.TryGetValue(col.ToLower());
if (currentprop.Ignore)
continue;
if (currentprop.Get != null)
{
DynamicSchemaColumn? 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
{
using (IDbTransaction trans = con.BeginTransaction())
{
foreach (IDynamicQueryBuilder builder in builders)
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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)
{
DynamicCachedReader cache = null;
using (IDbConnection con = Open())
using (IDbCommand cmd = con.CreateCommand())
{
using (IDataReader rdr = cmd
.SetCommand(sql)
.AddParameters(this, args)
.ExecuteReader())
cache = new DynamicCachedReader(rdr);
while (cache.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 = cache.RowToDynamic();
}
catch (ArgumentException argex)
{
StringBuilder 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)
{
DynamicCachedReader cache = null;
using (IDbConnection con = Open())
using (IDbCommand cmd = con.CreateCommand())
{
using (IDataReader rdr = cmd
.SetCommand(builder)
.ExecuteReader())
cache = new DynamicCachedReader(rdr);
while (cache.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 = cache.RowToDynamic();
}
catch (ArgumentException argex)
{
StringBuilder 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 (IDbConnection con = Open())
using (IDbCommand 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 (IDbConnection con = Open())
using (IDbCommand 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 (KeyValuePair> 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 (IDbConnection con = Open())
using (IDbCommand 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)
{
DataTable st = null;
using (IDataReader rdr = cmd.ExecuteReader(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo))
st = rdr.GetSchemaTable();
using (st)
foreach (DataRow col in st.Rows)
{
dynamic c = col.RowToDynamicUpper();
yield return new DynamicSchemaColumn
{
Name = c.COLUMNNAME,
Type = ReadSchemaType(c),
IsKey = c.ISKEY ?? false,
IsUnique = c.ISUNIQUE ?? false,
AllowNull = c.ALLOWNULL ?? false,
Size = (int)(c.COLUMNSIZE ?? 0),
Precision = (byte)(c.NUMERICPRECISION ?? 0),
Scale = (byte)(c.NUMERICSCALE ?? 0)
};
}
}
/// Reads the type of column from the schema.
/// The schema.
/// Generic parameter type.
protected virtual DbType ReadSchemaType(dynamic schema)
{
Type type = (Type)schema.DATATYPE;
// Small hack for SQL Server Provider
if (type == typeof(string) && Provider != null && Provider.GetType() == typeof(System.Data.SqlClient.SqlClientFactory))
{
var map = schema as IDictionary;
string typeName = (map.TryGetValue("DATATYPENAME") ?? string.Empty).ToString();
switch (typeName)
{
case "varchar":
return DbType.AnsiString;
case "nvarchar":
return DbType.String;
}
}
return DynamicExtensions.TypeMap.TryGetNullable(type) ?? DbType.String;
}
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,
AllowNull = DynamicExtensions.CoalesceNullable(
v.Value.Column != null ? v.Value.Column.AllowNull : true,
col.HasValue ? col.Value.AllowNull : true).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,
AllowNull = DynamicExtensions.CoalesceNullable(v.Value.Column != null ? v.Value.Column.AllowNull : true, true).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)
{
if (DumpCommandDelegate != null)
DumpCommandDelegate(cmd, cmd.DumpToString());
else
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))
{
List 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, () =>
{
Stack 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, () =>
{
Stack 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, () =>
{
Stack 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
List tables = TablesCache.Values.ToList();
TablesCache.Clear();
tables.ForEach(t => t.Dispose());
tables.Clear();
tables = null;
#endif
foreach (KeyValuePair> con in TransactionPool)
{
// Close all commands
if (CommandsPool.ContainsKey(con.Key))
{
List 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();
if (_proc != null)
_proc.Dispose();
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 supports limit offset syntax (SELECT FIRST x SKIP y ... FROM ...).
SupportFirstSkip = 0x00000020,
/// Database support standard schema.
SupportSchema = 0x00000010,
/// Database support stored procedures (EXEC procedure ...).
SupportStoredProcedures = 0x00000100,
/// 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; }
/// Gets the last access time.
public long Ticks { 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();
_lastProp.Ticks = DateTime.Now.Ticks;
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();
_lastProp.Ticks = DateTime.Now.Ticks;
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 (object item in args)
{
if (item is DynamicExpando)
cmd.AddParameters(database, (DynamicExpando)item);
else if (item is ExpandoObject)
cmd.AddParameters(database, (ExpandoObject)item);
else
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 (KeyValuePair 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 (KeyValuePair 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)
{
IDbDataParameter 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;
else if (p.DbType == DbType.AnsiString)
p.Size = item.ToString().Length > 8000 ? -1 : 8000;
}
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)
{
IDbDataParameter 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.AnsiString)
p.Size = value.ToString().Length > 8000 ? -1 : 8000;
else 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)
{
IDbDataParameter p = cmd.CreateParameter();
p.ParameterName = builder.Database.GetParameterName(cmd.Parameters.Count);
DynamicSchemaColumn? 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.AnsiString)
p.Size = item.Value.ToString().Length > 8000 ? -1 : 8000;
else 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 ?? DBNull.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 ?? DBNull.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 ?? DBNull.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)
{
try
{
((IDbDataParameter)command.Parameters[parameterName]).Value = value;
}
catch (Exception ex)
{
throw new ArgumentException(string.Format("Error setting parameter {0} in command {1}", parameterName, command.CommandText ?? string.Empty), ex);
}
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)
{
try
{
((IDbDataParameter)command.Parameters[index]).Value = value;
}
catch (Exception ex)
{
throw new ArgumentException(string.Format("Error setting parameter {0} in command {1}", index, command.CommandText ?? string.Empty), ex);
}
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
{
MethodInfo 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())
{
MethodInfo 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 (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 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.");
}
}
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)
{
StringBuilder 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)
{
try
{
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();
}
}
catch (NullReferenceException)
{
writer.WriteLine("Command disposed.");
}
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)
{
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)
{
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
{
IDynamicSelectQueryBuilder sub = b.SubQuery();
subquery(b, sub);
try
{
(b as DynamicQueryBuilder).ParseCommand(sub as DynamicQueryBuilder, b.Parameters);
}
catch (ArgumentException)
{
// This might occur if join was made to subquery
}
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
{
IDynamicSelectQueryBuilder 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)
{
List 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)
{
DynamicExpando result = new DynamicExpando();
IDictionary dict = (IDictionary)result;
foreach (KeyValuePair 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)
{
ExpandoObject result = new ExpandoObject();
IDictionary dict = (IDictionary)result;
foreach (KeyValuePair 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)
{
if (o == null)
return null;
Type ot = o.GetType();
if (ot == typeof(DynamicExpando) || ot == typeof(ExpandoObject))
return o;
DynamicExpando result = new DynamicExpando();
IDictionary 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)))
{
NameValueCollection nameValue = (NameValueCollection)o;
nameValue.Cast()
.Select(key => new KeyValuePair(key, nameValue[key]))
.ToList()
.ForEach(i => dict.Add(i));
}
else
{
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(ot);
if (mapper != null)
{
foreach (DynamicPropertyInvoker item in mapper.ColumnsMap.Values)
if (item.Get != null)
dict.Add(item.Name, item.Get(o));
}
else
{
PropertyInfo[] props = ot.GetProperties();
foreach (PropertyInfo 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)
{
Type ot = o.GetType();
if (ot == typeof(ExpandoObject) || ot == typeof(DynamicExpando))
return o;
ExpandoObject result = new ExpandoObject();
IDictionary 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)))
{
NameValueCollection nameValue = (NameValueCollection)o;
nameValue.Cast()
.Select(key => new KeyValuePair(key, nameValue[key]))
.ToList()
.ForEach(i => dict.Add(i));
}
else
{
DynamicTypeMap mapper = DynamicMapperCache.GetMapper(ot);
if (mapper != null)
{
foreach (DynamicPropertyInvoker item in mapper.ColumnsMap.Values)
if (item.Get != null)
dict.Add(item.Name, item.Get(o));
}
else
{
PropertyInfo[] props = ot.GetProperties();
foreach (PropertyInfo 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();
IDictionary 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();
IDictionary 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